From 54cd5fb8ebf11b71c676b72a32811873caef20de Mon Sep 17 00:00:00 2001 From: Maufeat Date: Sat, 29 Aug 2026 01:04:02 +0200 Subject: [PATCH] [core, hle, video_core, memory] Improve multi-process, display layers, dynamic shader cache and rework overlay (#4238) - [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions). - [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md) - [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability. ------------------- - Dynamic shader cache reloading capability for qlaunch - Multi-process improvements (thanks to @frank1734), instead of just using the main application we now respect caller process (also did it for HID devices while I was at it) - Layer stack masks & shared buffer screenshot for video core, added different masks (screenshot, recording, etc.) this was discovered as an issue due to how in qlaunch the transition was not right between applications. May not be perfect but also fixes screenshots while using qlaunch - Reworked overlay display management (input and visibility) - instead of random numbers as I've previously did, I decided to add an AppletZIndex enum for better readability. Also split capability of input by touch and gamepad. - etc. Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4238 Reviewed-by: Samuel Reviewed-by: CamilleLaVey Reviewed-by: MaranBr --- src/android/app/src/main/jni/native.cpp | 63 +++- src/android/app/src/main/jni/native.h | 6 + .../opus/opus_multistream_decode_object.cpp | 12 +- src/audio_core/opus/hardware_opus.cpp | 5 +- src/core/core.cpp | 45 ++- src/core/core.h | 9 + src/core/file_sys/content_archive.cpp | 4 +- src/core/file_sys/content_archive.h | 5 +- .../fssystem/fssystem_indirect_storage.cpp | 13 + .../fssystem/fssystem_indirect_storage.h | 12 + src/core/file_sys/nca_metadata.h | 4 +- src/core/file_sys/registered_cache.cpp | 35 +- src/core/file_sys/submission_package.cpp | 14 +- src/core/hle/kernel/kernel.cpp | 25 +- src/core/hle/kernel/kernel.h | 7 +- src/core/hle/service/am/am_results.h | 4 + src/core/hle/service/am/am_types.h | 10 +- src/core/hle/service/am/applet.cpp | 18 +- src/core/hle/service/am/applet.h | 8 +- .../hle/service/am/display_layer_manager.cpp | 57 +++- .../hle/service/am/display_layer_manager.h | 10 +- src/core/hle/service/am/hid_registration.cpp | 17 +- src/core/hle/service/am/hid_registration.h | 2 +- .../am/service/applet_common_functions.cpp | 4 +- .../am/service/application_functions.cpp | 47 ++- .../service/am/service/display_controller.cpp | 27 +- .../service/am/service/overlay_functions.cpp | 28 +- .../service/am/service/overlay_functions.h | 3 +- src/core/hle/service/am/window_system.cpp | 216 +++++++----- src/core/hle/service/am/window_system.h | 9 +- .../hle/service/aoc/addon_content_manager.cpp | 14 +- src/core/hle/service/bcat/bcat_service.cpp | 12 +- src/core/hle/service/bcat/bcat_service.h | 6 +- src/core/hle/service/bcat/service_creator.cpp | 9 +- src/core/hle/service/caps/caps_manager.cpp | 9 +- src/core/hle/service/caps/caps_su.cpp | 3 +- src/core/hle/service/hid/hid_server.cpp | 2 +- src/core/hle/service/hid/hidbus.cpp | 2 +- src/core/hle/service/hid/irs.cpp | 8 +- src/core/hle/service/jit/jit.cpp | 2 +- ...nly_application_control_data_interface.cpp | 5 +- .../service/nvdrv/devices/nvdisp_disp0.cpp | 1 + .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 19 +- .../hle/service/nvdrv/devices/nvhost_gpu.h | 9 +- src/core/hle/service/nvnflinger/display.h | 6 +- .../service/nvnflinger/hardware_composer.cpp | 1 + src/core/hle/service/nvnflinger/hwc_layer.h | 23 ++ .../service/nvnflinger/surface_flinger.cpp | 10 +- .../hle/service/nvnflinger/surface_flinger.h | 3 +- .../service/pctl/parental_control_service.cpp | 7 +- .../service/pctl/parental_control_service.h | 4 +- .../pctl/parental_control_service_factory.cpp | 16 +- src/core/hle/service/pm/pm.cpp | 7 +- src/core/hle/service/prepo/prepo.cpp | 4 +- src/core/hle/service/vi/container.cpp | 12 +- src/core/hle/service/vi/container.h | 3 +- .../hle/service/vi/shared_buffer_manager.cpp | 317 +++++++++++++----- .../hle/service/vi/shared_buffer_manager.h | 17 +- src/core/loader/loader.cpp | 39 +++ src/core/loader/nca.cpp | 46 ++- src/core/loader/nca.h | 12 +- src/core/loader/nso.cpp | 5 +- src/core/loader/nsp.cpp | 5 +- src/core/memory/cheat_engine.cpp | 37 +- src/core/memory/cheat_engine.h | 11 + src/core/reporter.cpp | 2 +- src/hid_core/hidbus/hidbus_base.cpp | 6 +- src/hid_core/hidbus/hidbus_base.h | 7 +- src/hid_core/hidbus/ringcon.cpp | 7 +- .../irsensor/image_transfer_processor.cpp | 29 +- .../irsensor/image_transfer_processor.h | 10 +- .../resources/six_axis/seven_six_axis.cpp | 13 +- .../resources/six_axis/seven_six_axis.h | 10 +- .../touch_screen/touch_screen_resource.cpp | 14 +- src/qt_common/render/emu_thread.cpp | 39 ++- src/qt_common/render/emu_thread.h | 17 + src/video_core/framebuffer_config.cpp | 24 ++ src/video_core/framebuffer_config.h | 15 + src/video_core/gpu.cpp | 10 + src/video_core/gpu.h | 2 + src/video_core/renderer_base.cpp | 4 +- src/video_core/renderer_base.h | 10 +- .../renderer_opengl/renderer_opengl.cpp | 13 +- .../renderer_opengl/renderer_opengl.h | 5 +- .../renderer_vulkan/renderer_vulkan.cpp | 13 +- .../renderer_vulkan/renderer_vulkan.h | 3 + .../renderer_vulkan/vk_pipeline_cache.cpp | 8 + src/yuzu/main_window.cpp | 82 ++++- src/yuzu/main_window.h | 1 + src/yuzu_cmd/yuzu.cpp | 27 ++ 90 files changed, 1410 insertions(+), 366 deletions(-) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 4d0c39f5c7..e8e9f0a14b 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -76,6 +76,7 @@ extern "C" { #include "core/frontend/applets/software_keyboard.h" #include "core/frontend/applets/web_browser.h" #include "common/android/applets/web_browser.h" +#include "core/file_sys/common_funcs.h" #include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/filesystem/filesystem.h" @@ -311,11 +312,23 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string ConfigureFilesystemProvider(filepath); // Load the ROM. + const u64 previous_program_id = + program_index != 0 && m_next_program_id.load() > + static_cast(Service::AM::AppletProgramId::MaxProgramId) + ? m_next_program_id.load() + : 0; + Service::AM::FrontendAppletParameters params{ + .program_id = previous_program_id, .applet_id = static_cast(m_applet_id), .launch_type = frontend_initiated ? Service::AM::LaunchType::FrontendInitiated : Service::AM::LaunchType::ApplicationInitiated, .program_index = static_cast(program_index), + .previous_program_index = + previous_program_id != 0 + ? static_cast(previous_program_id - + FileSys::GetBaseTitleID(previous_program_id)) + : -1, }; m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath, params); @@ -328,8 +341,12 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string m_system.GetCpuManager().OnGpuReady(); m_system.RegisterExitCallback([&] { HaltEmulation(); }); + m_system.RegisterApplicationChangedCallback( + [&](u64 changed_program_id) { RequestDiskShaderCacheReload(changed_program_id); }); + // Register an ExecuteProgram callback such that Core can execute a sub-program m_system.RegisterExecuteProgramCallback([&](std::size_t program_index_) { + m_next_program_id = m_system.GetApplicationProcessProgramID(); m_next_program_index = program_index_; EmulationSession::GetInstance().HaltEmulation(); }); @@ -407,20 +424,58 @@ void EmulationSession::RunEmulation() { } while (true) { + std::optional reload_title; { [[maybe_unused]] std::unique_lock lock(m_mutex); - if (m_cv.wait_for(lock, std::chrono::milliseconds(800), - [&]() { return !m_is_running; })) { - // Emulation halted. - break; + if (m_cv.wait_for(lock, std::chrono::milliseconds(800), [&]() { + return !m_is_running || m_pending_shader_cache_title.has_value(); + })) { + if (!m_is_running) { + break; + } + reload_title = std::exchange(m_pending_shader_cache_title, std::nullopt); } } + + if (reload_title.has_value()) + ReloadDiskShaderCache(*reload_title); } // Reset current applet ID. m_applet_id = static_cast(Service::AM::AppletId::Application); } +void EmulationSession::RequestDiskShaderCacheReload(u64 program_id) { + { + std::scoped_lock lock(m_mutex); + m_pending_shader_cache_title = program_id; + } + m_cv.notify_one(); +} + +void EmulationSession::ReloadDiskShaderCache(u64 program_id) { + if (!Settings::values.use_disk_shader_cache.GetValue()) + return; + + LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", program_id); + + const bool was_paused = m_is_paused; + + m_system.Pause(); + m_system.GPU().WaitForIdle(); + m_system.GPU().ObtainContext(); + + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); + m_system.Renderer().ReadRasterizer()->LoadDiskResources(program_id, std::stop_token{}, + LoadDiskCacheProgress); + LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); + + m_system.GPU().ReleaseContext(); + + if (!was_paused) + m_system.Run(); +} + Common::Android::SoftwareKeyboard::AndroidKeyboard* EmulationSession::SoftwareKeyboard() { return m_software_keyboard; } diff --git a/src/android/app/src/main/jni/native.h b/src/android/app/src/main/jni/native.h index 6b171d5a66..5794660bbd 100644 --- a/src/android/app/src/main/jni/native.h +++ b/src/android/app/src/main/jni/native.h @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include #include "common/android/applets/software_keyboard.h" #include "core/core.h" @@ -44,6 +46,7 @@ public: void HaltEmulation(); void RunEmulation(); void ShutdownEmulation(); + void RequestDiskShaderCacheReload(u64 program_id); const Core::PerfStatsResults& PerfStats(); int ShadersBuilding(); @@ -65,6 +68,7 @@ private: static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max); static void OnEmulationStopped(Core::SystemResultStatus result); static void ChangeProgram(std::size_t program_index); + void ReloadDiskShaderCache(u64 program_id); private: // Window management @@ -83,6 +87,7 @@ private: Common::Android::SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{}; std::unique_ptr m_manual_provider; int m_applet_id{1}; + std::optional m_pending_shader_cache_title; // GPU driver parameters std::shared_ptr m_vulkan_library; @@ -93,4 +98,5 @@ private: // Program index for next boot std::atomic m_next_program_index = -1; + std::atomic m_next_program_id = 0; }; diff --git a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp index 05cf3975db..6fecd63309 100644 --- a/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp +++ b/src/audio_core/adsp/apps/opus/opus_multistream_decode_object.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -7,13 +10,12 @@ namespace AudioCore::ADSP::OpusDecoder { namespace { -bool IsValidChannelCount(u32 channel_count) { - return channel_count == 1 || channel_count == 2; -} +constexpr u32 OpusStreamCountMax = 255; bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { - return total_stream_count > 0 && static_cast(stereo_stream_count) >= 0 && - stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); + return total_stream_count > 0 && total_stream_count <= OpusStreamCountMax && + static_cast(stereo_stream_count) >= 0 && + stereo_stream_count <= total_stream_count; } } // namespace diff --git a/src/audio_core/opus/hardware_opus.cpp b/src/audio_core/opus/hardware_opus.cpp index 30805f4a36..0037a3e503 100644 --- a/src/audio_core/opus/hardware_opus.cpp +++ b/src/audio_core/opus/hardware_opus.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -105,7 +108,7 @@ Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 chan shared_memory.host_send_data[4] = total_stream_count; shared_memory.host_send_data[5] = stereo_stream_count; - ASSERT(channel_count <= MaxChannels); + ASSERT(channel_count <= shared_memory.channel_mapping.size()); std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); opus_decoder.Send(ADSP::Direction::DSP, diff --git a/src/core/core.cpp b/src/core/core.cpp index d110aa5f8b..afcca1f806 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -295,6 +295,10 @@ struct System::Impl { SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) { InitializeKernel(system); + if (params.applet_type == Service::AM::AppletType::Application) { + current_application_filepath = filepath; + } + const auto file = GetGameFileFromPath(virtual_filesystem, filepath); // Create the application process @@ -330,7 +334,7 @@ struct System::Impl { LaunchTimestampCache::SaveLaunchTimestamp(params.program_id); // Make the process created be the application - kernel.MakeApplicationProcess(process->GetHandle()); + kernel.SetApplicationProcess(process->GetHandle()); // Set up the rest of the system. SystemResultStatus init_result{SetupForApplicationProcess(system, emu_window)}; @@ -467,6 +471,7 @@ struct System::Impl { Core::SpeedLimiter speed_limiter; ExecuteProgramCallback execute_program_callback; ExitCallback exit_callback; + ApplicationChangedCallback application_changed_callback; std::optional services; std::optional debugger; @@ -488,6 +493,8 @@ struct System::Impl { std::array dynarmic_ticks{}; std::array build_id{}; + std::string current_application_filepath; + /// Service manager std::shared_ptr service_manager; /// ContentProviderUnion instance @@ -727,7 +734,25 @@ const Core::SpeedLimiter& System::SpeedLimiter() const { } u64 System::GetApplicationProcessProgramID() const { - return impl->kernel.ApplicationProcess()->GetProgramId(); + const auto* const process = impl->kernel.ApplicationProcess(); + return process != nullptr ? process->GetProgramId() : 0; +} + +u64 System::GetProgramIdForProcessId(u64 process_id) const { + auto process = impl->kernel.GetProcessByProcessId(process_id); + return process.IsNull() ? 0 : process->GetProgramId(); +} + +u64 System::ResolveCallerProgramId(u64 process_id) const { + if (const auto program_id = this->GetProgramIdForProcessId(process_id); program_id != 0) { + return program_id; + } + + const auto fallback = this->GetApplicationProcessProgramID(); + LOG_WARNING(Core, + "Could not resolve caller process_id={}, falling back to application {:016X}", + process_id, fallback); + return fallback; } Loader::ResultStatus System::GetGameName(std::string& out) const { @@ -910,6 +935,10 @@ void System::ExecuteProgram(std::size_t program_index) { } } +const std::string& System::GetCurrentApplicationFilePath() const { + return impl->current_application_filepath; +} + /// @brief Gets a reference to the user channel stack. /// It is used to transfer data between programs. std::vector>& System::GetUserChannel() { @@ -961,6 +990,18 @@ void System::Exit() { } } +void System::RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback) { + impl->application_changed_callback = std::move(callback); +} + +void System::NotifyApplicationChanged(u64 program_id) { + //LOG_DEBUG(Core, "Running application changed to {:016X}", program_id); + + if (impl->application_changed_callback) { + impl->application_changed_callback(program_id); + } +} + void System::ApplySettings() { impl->RefreshTime(*this); diff --git a/src/core/core.h b/src/core/core.h index 93f7b057f7..d88ed64151 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -322,6 +322,10 @@ public: [[nodiscard]] u64 GetApplicationProcessProgramID() const; + [[nodiscard]] u64 GetProgramIdForProcessId(u64 process_id) const; + + [[nodiscard]] u64 ResolveCallerProgramId(u64 process_id) const; + /// Gets the name of the current game [[nodiscard]] Loader::ResultStatus GetGameName(std::string& out) const; @@ -422,6 +426,7 @@ public: void PushGeneralChannelData(std::vector&& data); bool TryPopGeneralChannel(std::vector& out_data); [[nodiscard]] Service::Event& GetGeneralChannelEvent(); + [[nodiscard]] const std::string& GetCurrentApplicationFilePath() const; /// Type used for the frontend to designate a callback for System to exit the application. using ExitCallback = std::function; @@ -435,6 +440,10 @@ public: /// Instructs the frontend to exit the application. void Exit(); + using ApplicationChangedCallback = std::function; + void RegisterApplicationChangedCallback(ApplicationChangedCallback&& callback); + void NotifyApplicationChanged(u64 program_id); + /// Applies any changes to settings to this core instance. void ApplySettings(); diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 2b478d6b99..b47b9437d0 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -29,7 +29,7 @@ static u8 MasterKeyIdForKeyGeneration(u8 key_generation) { return std::max(key_generation, 1) - 1; } -NCA::NCA(VirtualFile file_, const NCA* base_nca) +NCA::NCA(VirtualFile file_, const NCA* base_nca, bool allow_missing_base) : file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { if (file == nullptr) { status = Loader::ResultStatus::ErrorNullFile; @@ -110,7 +110,7 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca) } } - if (is_update && base_nca == nullptr) { + if (is_update && base_nca == nullptr && !allow_missing_base) { status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS; } else { status = Loader::ResultStatus::Success; diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 8560617f5b..b88fcdc4ee 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -63,7 +66,7 @@ inline bool IsDirectoryLogoPartition(const VirtualDir& pfs) { // After construction, use GetStatus to determine if the file is valid and ready to be used. class NCA : public ReadOnlyVfsDirectory { public: - explicit NCA(VirtualFile file, const NCA* base_nca = nullptr); + explicit NCA(VirtualFile file, const NCA* base_nca = nullptr, bool allow_missing_base = false); ~NCA() override; Loader::ResultStatus GetStatus() const; diff --git a/src/core/file_sys/fssystem/fssystem_indirect_storage.cpp b/src/core/file_sys/fssystem/fssystem_indirect_storage.cpp index 41f5b75b6e..e66ce49d9a 100644 --- a/src/core/file_sys/fssystem/fssystem_indirect_storage.cpp +++ b/src/core/file_sys/fssystem/fssystem_indirect_storage.cpp @@ -4,6 +4,7 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/logging.h" #include "core/file_sys/errors.h" #include "core/file_sys/fssystem/fssystem_indirect_storage.h" @@ -99,6 +100,18 @@ Result IndirectStorage::GetEntryList(Entry* out_entries, s32* out_entry_count, s R_SUCCEED(); } +void IndirectStorage::ReportMissingOriginal(s64 offset, s64 size) { + if (m_reported_missing_original) { + return; + } + m_reported_missing_original = true; + + LOG_ERROR(Common_Filesystem, + "Patch storage requests {:#x} bytes at {:#x} from a base storage that is not " + "present; this data will read as garbage", + size, offset); +} + size_t IndirectStorage::Read(u8* buffer, size_t size, size_t offset) const { // Validate pre-conditions. ASSERT(this->IsInitialized()); diff --git a/src/core/file_sys/fssystem/fssystem_indirect_storage.h b/src/core/file_sys/fssystem/fssystem_indirect_storage.h index cc7b5cd718..81f94e2e19 100644 --- a/src/core/file_sys/fssystem/fssystem_indirect_storage.h +++ b/src/core/file_sys/fssystem/fssystem_indirect_storage.h @@ -85,6 +85,9 @@ public: void SetStorage(s32 idx, VirtualFile storage) { ASSERT(0 <= idx && idx < StorageCount); m_data_storage[idx] = storage; + if (idx == 0) { + m_original_missing = storage == nullptr || storage->GetSize() == 0; + } } template @@ -130,6 +133,9 @@ protected: template Result OperatePerEntry(s64 offset, s64 size, F func); + // Launching another game makes the original storage inaccessable. + // This is a helper for multi-nca games. + void ReportMissingOriginal(s64 offset, s64 size); private: struct ContinuousReadingEntry { @@ -154,6 +160,8 @@ private: private: mutable BucketTree m_table; std::array m_data_storage; + bool m_original_missing{false}; + bool m_reported_missing_original{false}; }; template @@ -272,6 +280,10 @@ Result IndirectStorage::OperatePerEntry(s64 offset, s64 size, F func) { if (needs_operate) { const auto cur_entry_phys_offset = cur_entry.GetPhysicalOffset(); + if (cur_entry.storage_index == 0 && m_original_missing) { + this->ReportMissingOriginal(cur_offset, cur_size); + } + if constexpr (RangeCheck) { // Get the current data storage's size. s64 cur_data_storage_size = m_data_storage[cur_entry.storage_index]->GetSize(); diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index 43437d0dd7..07aae1642e 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -48,11 +48,11 @@ struct ContentRecord { std::array nca_id; std::array size; ContentRecordType type; - INSERT_PADDING_BYTES(1); + u8 id_offset; }; static_assert(sizeof(ContentRecord) == 0x38, "ContentRecord has incorrect size."); -constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, {}}; +constexpr ContentRecord EMPTY_META_CONTENT_RECORD{{}, {}, {}, ContentRecordType::Meta, 0}; struct MetaRecord { u64_le title_id; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 9e9db0ed4d..83f5e265dd 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -567,20 +567,45 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { } static std::optional CheckMapForContentRecord(const ankerl::unordered_dense::map& map, u64 title_id, ContentRecordType type) { - const auto cmnt_iter = map.find(title_id); + auto cmnt_iter = map.find(title_id); + u8 id_offset = 0; + if (cmnt_iter == map.cend()) { - return std::nullopt; + const auto program_index = title_id & AOC_TITLE_ID_MASK; + if (program_index == 0) { + return std::nullopt; + } + + cmnt_iter = map.find(title_id & ~AOC_TITLE_ID_MASK); + if (cmnt_iter == map.cend()) { + return std::nullopt; + } + + id_offset = static_cast(program_index); } const auto& cnmt = cmnt_iter->second; const auto& content_records = cnmt.GetContentRecords(); const auto iter = std::find_if(content_records.cbegin(), content_records.cend(), - [type](const ContentRecord& rec) { return rec.type == type; }); - if (iter == content_records.cend()) { + [type, id_offset](const ContentRecord& rec) { + return rec.type == type && rec.id_offset == id_offset; + }); + if (iter != content_records.cend()) { + return std::make_optional(iter->nca_id); + } + + if (id_offset != 0) { return std::nullopt; } - return std::make_optional(iter->nca_id); + const auto fallback_iter = + std::find_if(content_records.cbegin(), content_records.cend(), + [type](const ContentRecord& rec) { return rec.type == type; }); + if (fallback_iter == content_records.cend()) { + return std::nullopt; + } + + return std::make_optional(fallback_iter->nca_id); } std::optional RegisteredCache::GetNcaIDFromMetadata(u64 title_id, diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 630bb0b079..0ea323492b 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -12,6 +12,7 @@ #include "common/hex_util.h" #include "common/logging.h" #include "core/crypto/key_manager.h" +#include "core/file_sys/common_funcs.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/partition_filesystem.h" @@ -65,10 +66,12 @@ u64 NSP::GetProgramTitleID() const { } auto program_id = expected_program_id; - if (program_id == 0) { - if (!program_status.empty()) { - program_id = program_status.begin()->first; - } + if (program_id == 0 && !program_status.empty()) { + program_id = std::min_element(program_status.cbegin(), program_status.cend(), + [](const auto& lhs, const auto& rhs) { + return lhs.first < rhs.first; + }) + ->first; } program_id = program_id + program_index; @@ -273,8 +276,7 @@ void NSP::ReadNCAs(const std::vector& files) { // If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and // 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular // update NCA. - if ((next_nca->GetTitleId() & 0x7FF) != 0 && - (next_nca->GetTitleId() & 0x800) == 0) { + if ((next_nca->GetTitleId() & AOC_TITLE_ID_MASK) != 0) { ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = std::move(next_nca); } else { diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d25ac37df4..a739e7020c 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -349,9 +349,18 @@ struct KernelCore::Impl { object_name_global_data.emplace(kernel); } - void MakeApplicationProcess(KernelCore& kernel, KProcess* process) { + void SetApplicationProcess(KernelCore& kernel, KProcess* process) { + if (application_process == process) + return; + + KProcess* const previous = application_process; application_process = process; - application_process->Open(kernel); + + if (application_process != nullptr) + application_process->Open(kernel); + + if (previous != nullptr) + previous->Close(kernel); } /// Sets the host thread ID for the caller. @@ -879,8 +888,8 @@ void KernelCore::RemoveProcess(KProcess* process) { } } -void KernelCore::MakeApplicationProcess(KProcess* process) { - impl->MakeApplicationProcess(*this, process); +void KernelCore::SetApplicationProcess(KProcess* process) { + impl->SetApplicationProcess(*this, process); } KProcess* KernelCore::ApplicationProcess() { @@ -891,6 +900,14 @@ const KProcess* KernelCore::ApplicationProcess() const { return impl->application_process; } +KScopedAutoObject KernelCore::GetProcessByProcessId(u64 process_id) { + std::scoped_lock lk{impl->process_list_lock}; + for (auto* const process : impl->process_list) + if (process != nullptr && process->GetProcessId() == process_id) + return {*this, process}; + return {*this, nullptr}; +} + std::list> KernelCore::GetProcessList() { std::list> processes; std::scoped_lock lk{impl->process_list_lock}; diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 3b754e9397..04790c8b38 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -124,8 +124,8 @@ public: void AppendNewProcess(KProcess* process); void RemoveProcess(KProcess* process); - /// Makes the given process the new application process. - void MakeApplicationProcess(KProcess* process); + /// Makes the given process the current application process. + void SetApplicationProcess(KProcess* process); /// Retrieves a pointer to the application process. KProcess* ApplicationProcess(); @@ -133,6 +133,9 @@ public: /// Retrieves a const pointer to the application process. const KProcess* ApplicationProcess() const; + /// Retrieves the process with the given process ID, or a null object. + KScopedAutoObject GetProcessByProcessId(u64 process_id); + /// Retrieves the list of processes. std::list> GetProcessList(); diff --git a/src/core/hle/service/am/am_results.h b/src/core/hle/service/am/am_results.h index 44846aa2e5..d1b86c9546 100644 --- a/src/core/hle/service/am/am_results.h +++ b/src/core/hle/service/am/am_results.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -10,6 +13,7 @@ namespace Service::AM { constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2}; constexpr Result ResultNoMessages{ErrorModule::AM, 3}; constexpr Result ResultLibraryAppletTerminated{ErrorModule::AM, 22}; +constexpr Result ResultApplicationRecordNotFound{ErrorModule::AM, 37}; constexpr Result ResultInvalidOffset{ErrorModule::AM, 503}; constexpr Result ResultInvalidStorageType{ErrorModule::AM, 511}; constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512}; diff --git a/src/core/hle/service/am/am_types.h b/src/core/hle/service/am/am_types.h index beb52b74dd..f88f91ae47 100644 --- a/src/core/hle/service/am/am_types.h +++ b/src/core/hle/service/am/am_types.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -201,6 +201,14 @@ enum class ProgramSpecifyKind : u32 { RestartProgram = 2, }; +// Maufeat: Use enums for zindex instead of using random zindex numbers +enum AppletZIndex : s32 { + Background = 0, + Foreground = 1, + ForegroundVisible = 2, + Overlay = 3, +}; + struct CommonArguments { CommonArgumentVersion arguments_version; CommonArgumentSize size; diff --git a/src/core/hle/service/am/applet.cpp b/src/core/hle/service/am/applet.cpp index efedcc5692..58d038348f 100644 --- a/src/core/hle/service/am/applet.cpp +++ b/src/core/hle/service/am/applet.cpp @@ -56,22 +56,22 @@ void Applet::UpdateSuspensionStateLocked(bool force_message) { } } -void Applet::SetInteractibleLocked(bool interactible) { - if (is_interactible == interactible) { +void Applet::SetInteractibleLocked(bool pad_interactible, bool touch_interactible) { + if (is_pad_interactible == pad_interactible && is_touch_interactible == touch_interactible) { return; } - is_interactible = interactible; + is_pad_interactible = pad_interactible; + is_touch_interactible = touch_interactible; const bool exit_requested = lifecycle_manager.GetExitRequested(); - const bool input_enabled = interactible && !exit_requested; + const bool pad_enabled = pad_interactible && !exit_requested; + const bool touch_enabled = touch_interactible && !exit_requested; - if (applet_id == AppletId::OverlayDisplay || applet_id == AppletId::Application) { - LOG_DEBUG(Service_AM, "called, applet={} interactible={} exit_requested={} input_enabled={} overlay_in_foreground={}", - static_cast(applet_id), interactible, exit_requested, input_enabled, overlay_in_foreground); - } + LOG_DEBUG(Service_AM, "applet={} pad={} touch={} exit_requested={}", + static_cast(applet_id), pad_enabled, touch_enabled, exit_requested); - hid_registration.EnableAppletToGetInput(input_enabled); + hid_registration.EnableAppletToGetInput(pad_enabled, touch_enabled); } void Applet::OnProcessTerminatedLocked() { diff --git a/src/core/hle/service/am/applet.h b/src/core/hle/service/am/applet.h index 9a3d7c7c7b..73303b2e66 100644 --- a/src/core/hle/service/am/applet.h +++ b/src/core/hle/service/am/applet.h @@ -125,9 +125,11 @@ struct Applet { bool album_image_taken_notification_enabled{}; bool record_volume_muted{}; bool is_activity_runnable{}; - bool is_interactible{true}; + bool is_pad_interactible{true}; + bool is_touch_interactible{true}; bool window_visible{true}; - bool overlay_in_foreground{false}; + bool overlay_watching_short_home_button{false}; + bool overlay_handling_touch_input{false}; // Events Event overlay_event; @@ -148,7 +150,7 @@ struct Applet { // Process state management void UpdateSuspensionStateLocked(bool force_message); - void SetInteractibleLocked(bool interactible); + void SetInteractibleLocked(bool pad_interactible, bool touch_interactible); void OnProcessTerminatedLocked(); }; diff --git a/src/core/hle/service/am/display_layer_manager.cpp b/src/core/hle/service/am/display_layer_manager.cpp index 6789629310..4f26794831 100644 --- a/src/core/hle/service/am/display_layer_manager.cpp +++ b/src/core/hle/service/am/display_layer_manager.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -6,6 +6,7 @@ #include "core/core.h" #include "core/hle/service/am/display_layer_manager.h" +#include "core/hle/service/nvnflinger/hwc_layer.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/vi/application_display_service.h" #include "core/hle/service/vi/container.h" @@ -33,6 +34,7 @@ void DisplayLayerManager::Initialize(Core::System& system, Kernel::KProcess* pro m_system_shared_buffer_id = 0; m_system_shared_layer_id = 0; m_applet_id = applet_id; + m_library_applet_mode = mode; m_buffer_sharing_enabled = false; m_blending_enabled = mode == LibraryAppletMode::PartialForeground || mode == LibraryAppletMode::PartialForegroundIndirectDisplay; @@ -72,14 +74,16 @@ Result DisplayLayerManager::CreateManagedDisplayLayer(u64* out_layer_id) { out_layer_id, 0, display_id, Service::AppletResourceUserId{m_process->GetProcessId()})); m_manager_display_service->SetLayerVisibility(m_visible, *out_layer_id); + (void)m_display_service->GetContainer()->SetLayerStackMask(*out_layer_id, + this->GetLayerStackMask()); if (m_applet_id != AppletId::Application) { (void)m_manager_display_service->SetLayerBlending(m_blending_enabled, *out_layer_id); if (m_applet_id == AppletId::OverlayDisplay) { - (void)m_manager_display_service->SetLayerZIndex(-1, *out_layer_id); + (void)m_manager_display_service->SetLayerZIndex(Overlay, *out_layer_id); (void)m_display_service->GetContainer()->SetLayerIsOverlay(*out_layer_id, true); } else { - (void)m_manager_display_service->SetLayerZIndex(1, *out_layer_id); + (void)m_manager_display_service->SetLayerZIndex(Foreground, *out_layer_id); } } @@ -122,10 +126,12 @@ Result DisplayLayerManager::IsSystemBufferSharingEnabled() { // Ensure the overlay layer is visible m_manager_display_service->SetLayerVisibility(m_visible, m_system_shared_layer_id); + (void)m_display_service->GetContainer()->SetLayerStackMask(m_system_shared_layer_id, + this->GetLayerStackMask()); m_manager_display_service->SetLayerBlending(m_blending_enabled, m_system_shared_layer_id); - s32 initial_z = 1; + s32 initial_z = Foreground; if (m_applet_id == AppletId::OverlayDisplay) { - initial_z = -1; + initial_z = Overlay; (void)m_display_service->GetContainer()->SetLayerIsOverlay(m_system_shared_layer_id, true); } m_manager_display_service->SetLayerZIndex(initial_z, m_system_shared_layer_id); @@ -142,6 +148,36 @@ Result DisplayLayerManager::GetSystemSharedLayerHandle(u64* out_system_shared_bu R_SUCCEED(); } +u32 DisplayLayerManager::GetLayerStackMask() const { + using Nvnflinger::LayerStackBit; + using Nvnflinger::LayerStackId; + + constexpr u32 Displayed = LayerStackBit(LayerStackId::Default); + constexpr u32 Screenshot = LayerStackBit(LayerStackId::Screenshot); + constexpr u32 Recording = LayerStackBit(LayerStackId::Recording); + constexpr u32 LastFrame = LayerStackBit(LayerStackId::LastFrame); + constexpr u32 Debug = LayerStackBit(LayerStackId::ApplicationForDebug); + + switch (m_applet_id) { + case AppletId::Application: + return Displayed | Screenshot | Recording | LastFrame | Debug; + case AppletId::OverlayDisplay: + return Displayed; + case AppletId::QLaunch: + return Displayed; + default: + break; + } + + switch (m_library_applet_mode) { + case LibraryAppletMode::AllForeground: + case LibraryAppletMode::AllForegroundInitiallyHidden: + return Displayed | Screenshot | LastFrame; + default: + return Displayed | Screenshot; + } +} + void DisplayLayerManager::SetWindowVisibility(bool visible) { if (m_visible == visible) { return; @@ -185,10 +221,17 @@ void DisplayLayerManager::SetOverlayZIndex(s32 z_index) { } Result DisplayLayerManager::WriteAppletCaptureBuffer(bool* out_was_written, - s32* out_fbshare_layer_index) { + s32* out_fbshare_layer_index, + VI::CaptureKind kind) { R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied); R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->WriteAppletCaptureBuffer( - out_was_written, out_fbshare_layer_index)); + out_was_written, out_fbshare_layer_index, kind)); +} + +Result DisplayLayerManager::ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color) { + R_UNLESS(m_buffer_sharing_enabled, VI::ResultPermissionDenied); + R_RETURN(m_display_service->GetContainer()->GetSharedBufferManager()->ClearAppletCaptureBuffer( + fbshare_layer_index, color)); } } // namespace Service::AM diff --git a/src/core/hle/service/am/display_layer_manager.h b/src/core/hle/service/am/display_layer_manager.h index 423eb432fa..e9f48476d2 100644 --- a/src/core/hle/service/am/display_layer_manager.h +++ b/src/core/hle/service/am/display_layer_manager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -23,6 +23,7 @@ class KProcess; namespace Service::VI { class IApplicationDisplayService; class IManagerDisplayService; +enum class CaptureKind : u32; } // namespace Service::VI namespace Service::AM { @@ -48,9 +49,13 @@ public: void SetOverlayZIndex(s32 z_index); - Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index); + Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_fbshare_layer_index, + VI::CaptureKind kind); + Result ClearAppletCaptureBuffer(s32 fbshare_layer_index, u32 color); private: + u32 GetLayerStackMask() const; + Kernel::KProcess* m_process{}; std::shared_ptr m_display_service{}; std::shared_ptr m_manager_display_service{}; @@ -59,6 +64,7 @@ private: u64 m_system_shared_buffer_id{}; u64 m_system_shared_layer_id{}; AppletId m_applet_id{}; + LibraryAppletMode m_library_applet_mode{}; bool m_buffer_sharing_enabled{}; bool m_blending_enabled{}; bool m_visible{true}; diff --git a/src/core/hle/service/am/hid_registration.cpp b/src/core/hle/service/am/hid_registration.cpp index 2613fc29a4..8ce9ae748b 100644 --- a/src/core/hle/service/am/hid_registration.cpp +++ b/src/core/hle/service/am/hid_registration.cpp @@ -36,12 +36,17 @@ HidRegistration::~HidRegistration() { } } -void HidRegistration::EnableAppletToGetInput(bool enable) { - if (m_process.IsInitialized()) { - m_hid_server->GetResourceManager()->SetAruidValidForVibration(m_process.GetProcessId(), - enable); - m_hid_server->GetResourceManager()->EnableInput(m_process.GetProcessId(), enable); - } +void HidRegistration::EnableAppletToGetInput(bool enable_pad, bool enable_touch) { + if (!m_process.IsInitialized()) + return; + + const auto resource_manager = m_hid_server->GetResourceManager(); + const u64 aruid = m_process.GetProcessId(); + + resource_manager->EnablePadInput(aruid, enable_pad); + resource_manager->EnableTouchScreen(aruid, enable_touch); + + resource_manager->SetAruidValidForVibration(aruid, enable_pad); } } // namespace Service::AM diff --git a/src/core/hle/service/am/hid_registration.h b/src/core/hle/service/am/hid_registration.h index 560266144f..59184b0bee 100644 --- a/src/core/hle/service/am/hid_registration.h +++ b/src/core/hle/service/am/hid_registration.h @@ -28,7 +28,7 @@ public: ~HidRegistration(); void RegisterCurrentProcess(); - void EnableAppletToGetInput(bool enable); + void EnableAppletToGetInput(bool enable_pad, bool enable_touch); private: Process& m_process; diff --git a/src/core/hle/service/am/service/applet_common_functions.cpp b/src/core/hle/service/am/service/applet_common_functions.cpp index ee536221d9..5ffec23b3e 100644 --- a/src/core/hle/service/am/service/applet_common_functions.cpp +++ b/src/core/hle/service/am/service/applet_common_functions.cpp @@ -4,6 +4,8 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/core.h" +#include "core/file_sys/common_funcs.h" #include "core/hle/service/am/applet.h" #include "core/hle/service/am/service/applet_common_functions.h" #include "core/hle/service/cmif_serialization.h" @@ -78,7 +80,7 @@ Result IAppletCommonFunctions::SetCpuBoostRequestPriority(s32 priority) { Result IAppletCommonFunctions::GetCurrentApplicationId(Out out_application_id) { LOG_WARNING(Service_AM, "(STUBBED) called"); - *out_application_id = system.GetApplicationProcessProgramID() & ~0xFFFULL; + *out_application_id = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID()); R_SUCCEED(); } diff --git a/src/core/hle/service/am/service/application_functions.cpp b/src/core/hle/service/am/service/application_functions.cpp index 03d88aeeb3..215d204098 100644 --- a/src/core/hle/service/am/service/application_functions.cpp +++ b/src/core/hle/service/am/service/application_functions.cpp @@ -6,8 +6,10 @@ #include +#include "common/hex_util.h" #include "common/settings.h" #include "common/uuid.h" +#include "core/file_sys/common_funcs.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" @@ -28,6 +30,27 @@ namespace Service::AM { +namespace { + +FileSys::PatchManager::Metadata GetApplicationMetadata(Core::System& system, u64 program_id) { + auto metadata = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, program_id); + if (metadata.first != nullptr) { + return metadata; + } + + const auto application_id = FileSys::GetBaseTitleID(program_id); + if (application_id == program_id) { + return metadata; + } + + LOG_DEBUG(Service_AM, + "no metadata for program_id={:016X}, falling back to application_id={:016X}", + program_id, application_id); + return FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, application_id); +} + +} // Anonymous namespace + IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr applet) : ServiceFramework{system_, "IApplicationFunctions"}, m_applet{std::move(applet)} { // clang-format off @@ -156,7 +179,7 @@ Result IApplicationFunctions::GetDesiredLanguage(Out out_language_code) { // Default to 0 (all languages supported) u32 supported_languages = 0; - const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id); + const auto res = GetApplicationMetadata(system, m_applet->program_id); if (res.first != nullptr) { supported_languages = res.first->GetSupportedLanguages(); } @@ -194,7 +217,7 @@ Result IApplicationFunctions::SetTerminateResult(Result terminate_result) { Result IApplicationFunctions::GetDisplayVersion(Out out_display_version) { LOG_DEBUG(Service_AM, "called"); - const auto res = FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, m_applet->program_id); + const auto res = GetApplicationMetadata(system, m_applet->program_id); if (res.first != nullptr) { const auto& version = res.first->GetVersionString(); std::memcpy(out_display_version->string.data(), version.data(), @@ -431,15 +454,21 @@ Result IApplicationFunctions::ExecuteProgram(ProgramSpecifyKind kind, u64 value) // https://switchbrew.org/wiki/Applet_Manager_services#CreateApplicationAndRequestToStart Result IApplicationFunctions::CreateApplicationAndRequestToStart(u64 application_id) { - LOG_INFO(Service_AM, "called, application_id={:016X}", application_id); + LOG_INFO(Service_AM, "called, application_id={:016X} current_program_id={:016X}", application_id, m_applet->program_id); - // If application_id is 0, relaunch the current application - const u64 target_application_id = - (application_id == 0) ? m_applet->program_id : application_id; + if (application_id == 0 || + FileSys::GetBaseTitleID(application_id) == FileSys::GetBaseTitleID(m_applet->program_id)) { + const auto program_index = application_id == 0 + ? 0 + : application_id - FileSys::GetBaseTitleID(application_id); - system.GetUserChannel() = m_applet->user_channel_launch_parameter; - system.ExecuteProgram(target_application_id); - R_SUCCEED(); + system.GetUserChannel() = m_applet->user_channel_launch_parameter; + system.ExecuteProgram(program_index); + R_SUCCEED(); + } + + LOG_ERROR(Service_AM, "Launching a different application ({:016X}) is not implemented!", application_id); + R_THROW(ResultUnknown); } Result IApplicationFunctions::ClearUserChannel() { diff --git a/src/core/hle/service/am/service/display_controller.cpp b/src/core/hle/service/am/service/display_controller.cpp index a02f012a92..8f87327b17 100644 --- a/src/core/hle/service/am/service/display_controller.cpp +++ b/src/core/hle/service/am/service/display_controller.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -8,6 +8,7 @@ #include "core/hle/service/am/applet.h" #include "core/hle/service/am/service/display_controller.h" #include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/vi/shared_buffer_manager.h" namespace Service::AM { @@ -71,16 +72,16 @@ Result IDisplayController::TakeScreenShotOfOwnLayer(bool unknown0, s32 fbshare_l } Result IDisplayController::ClearCaptureBuffer(bool unknown0, s32 fbshare_layer_index, u32 color) { - LOG_WARNING(Service_AM, "(STUBBED) called, unknown0={} fbshare_layer_index={} color={:#x}", - unknown0, fbshare_layer_index, color); - R_SUCCEED(); + LOG_DEBUG(Service_AM, "called, unknown0={} fbshare_layer_index={} color={:#x}", unknown0, + fbshare_layer_index, color); + R_RETURN(applet->display_layer_manager.ClearAppletCaptureBuffer(fbshare_layer_index, color)); } Result IDisplayController::AcquireLastForegroundCaptureSharedBuffer( Out out_was_written, Out out_fbshare_layer_index) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written, - out_fbshare_layer_index)); + LOG_DEBUG(Service_AM, "called"); + R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer( + out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastForeground)); } Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() { @@ -90,9 +91,9 @@ Result IDisplayController::ReleaseLastForegroundCaptureSharedBuffer() { Result IDisplayController::AcquireCallerAppletCaptureSharedBuffer( Out out_was_written, Out out_fbshare_layer_index) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written, - out_fbshare_layer_index)); + LOG_DEBUG(Service_AM, "called"); + R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer( + out_was_written, out_fbshare_layer_index, VI::CaptureKind::CallerApplet)); } Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() { @@ -102,9 +103,9 @@ Result IDisplayController::ReleaseCallerAppletCaptureSharedBuffer() { Result IDisplayController::AcquireLastApplicationCaptureSharedBuffer( Out out_was_written, Out out_fbshare_layer_index) { - LOG_WARNING(Service_AM, "(STUBBED) called"); - R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer(out_was_written, - out_fbshare_layer_index)); + LOG_DEBUG(Service_AM, "called"); + R_RETURN(applet->display_layer_manager.WriteAppletCaptureBuffer( + out_was_written, out_fbshare_layer_index, VI::CaptureKind::LastApplication)); } Result IDisplayController::ReleaseLastApplicationCaptureSharedBuffer() { diff --git a/src/core/hle/service/am/service/overlay_functions.cpp b/src/core/hle/service/am/service/overlay_functions.cpp index 41f2e5c008..8150b5dd07 100644 --- a/src/core/hle/service/am/service/overlay_functions.cpp +++ b/src/core/hle/service/am/service/overlay_functions.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include "core/hle/service/am/applet.h" @@ -22,7 +22,7 @@ namespace Service::AM { {10, nullptr, "StartShutdownSequenceForOverlay"}, {11, nullptr, "StartRebootSequenceForOverlay"}, {20, D<&IOverlayFunctions::SetHandlingHomeButtonShortPressedEnabled>, "SetHandlingHomeButtonShortPressedEnabled"}, - {21, nullptr, "SetHandlingTouchScreenInputEnabled"}, + {21, D<&IOverlayFunctions::SetHandlingTouchScreenInputEnabled>, "SetHandlingTouchScreenInputEnabled"}, {30, nullptr, "SetHealthWarningShowingState"}, {31, D<&IOverlayFunctions::IsHealthWarningRequired>, "IsHealthWarningRequired"}, {40, nullptr, "GetApplicationNintendoLogo"}, @@ -43,10 +43,12 @@ namespace Service::AM { Result IOverlayFunctions::BeginToWatchShortHomeButtonMessage() { LOG_DEBUG(Service_AM, "called"); - m_applet->overlay_in_foreground = true; - m_applet->home_button_short_pressed_blocked = false; + { + std::scoped_lock lk{m_applet->lock}; + m_applet->overlay_watching_short_home_button = true; + } - if (auto *window_system = system.GetAppletManager().GetWindowSystem()) { + if (auto* window_system = system.GetAppletManager().GetWindowSystem()) { window_system->RequestUpdate(); } @@ -56,16 +58,26 @@ namespace Service::AM { Result IOverlayFunctions::EndToWatchShortHomeButtonMessage() { LOG_DEBUG(Service_AM, "called"); - m_applet->overlay_in_foreground = false; - m_applet->home_button_short_pressed_blocked = false; + { + std::scoped_lock lk{m_applet->lock}; + m_applet->overlay_watching_short_home_button = false; + } - if (auto *window_system = system.GetAppletManager().GetWindowSystem()) { + if (auto* window_system = system.GetAppletManager().GetWindowSystem()) { window_system->RequestUpdate(); } R_SUCCEED(); } + Result IOverlayFunctions::SetHandlingTouchScreenInputEnabled(bool enabled) { + LOG_DEBUG(Service_AM, "called, enabled={}", enabled); + + std::scoped_lock lk{m_applet->lock}; + m_applet->overlay_handling_touch_input = enabled; + R_SUCCEED(); + } + Result IOverlayFunctions::GetApplicationIdForLogo(Out out_application_id) { LOG_DEBUG(Service_AM, "called"); diff --git a/src/core/hle/service/am/service/overlay_functions.h b/src/core/hle/service/am/service/overlay_functions.h index 870f5eae9c..eefd8d862b 100644 --- a/src/core/hle/service/am/service/overlay_functions.h +++ b/src/core/hle/service/am/service/overlay_functions.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #pragma once @@ -20,6 +20,7 @@ namespace Service::AM { Result SetAutoSleepTimeAndDimmingTimeEnabled(bool enabled); Result IsHealthWarningRequired(Out is_required); Result SetHandlingHomeButtonShortPressedEnabled(bool enabled); + Result SetHandlingTouchScreenInputEnabled(bool enabled); Result Unknown70(); private: diff --git a/src/core/hle/service/am/window_system.cpp b/src/core/hle/service/am/window_system.cpp index bbaea8ad12..3858791430 100644 --- a/src/core/hle/service/am/window_system.cpp +++ b/src/core/hle/service/am/window_system.cpp @@ -4,7 +4,11 @@ // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + +#include "common/settings.h" #include "core/core.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/service/am/am_results.h" #include "core/hle/service/am/applet.h" #include "core/hle/service/am/applet_manager.h" @@ -32,46 +36,82 @@ void WindowSystem::RequestUpdate() { } void WindowSystem::Update() { - std::scoped_lock lk{m_lock}; + { + std::scoped_lock lk{m_lock}; - LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}", - m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr); + LOG_DEBUG(Service_AM, "called, home_menu={} application={} overlay={}", + m_home_menu != nullptr, m_application != nullptr, m_overlay_display != nullptr); - // Loop through all applets and remove terminated applets. - this->PruneTerminatedAppletsLocked(); + // Loop through all applets and remove terminated applets. + this->PruneTerminatedAppletsLocked(); - // If the home menu is being locked into the foreground, handle that. - if (this->LockHomeMenuIntoForegroundLocked()) { - return; + // If the home menu is being locked into the foreground, handle that. + if (!this->LockHomeMenuIntoForegroundLocked()) { + const bool overlay_takes_input = this->DoesOverlayTakeInputLocked(); + + this->UpdateAppletStateLocked(m_overlay_display, true, overlay_takes_input); + this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_takes_input); + this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_takes_input); + } } - bool overlay_blocks_input = false; - if (m_overlay_display) { - std::scoped_lock lk_overlay{m_overlay_display->lock}; - overlay_blocks_input = m_overlay_display->overlay_in_foreground; - } - - // Recursively update each applet root. - this->UpdateAppletStateLocked(m_home_menu, m_foreground_requested_applet == m_home_menu, overlay_blocks_input); - this->UpdateAppletStateLocked(m_application, m_foreground_requested_applet == m_application, overlay_blocks_input); - this->UpdateAppletStateLocked(m_overlay_display, true, false); // overlay is always updated, never blocked + this->NotifyApplicationChangedIfNeeded(); } void WindowSystem::TrackApplet(std::shared_ptr applet, bool is_application) { - std::scoped_lock lk{m_lock}; + { + std::scoped_lock lk{m_lock}; - if (applet->applet_id == AppletId::QLaunch) { - ASSERT(m_home_menu == nullptr); - m_home_menu = applet.get(); - } else if (applet->applet_id == AppletId::OverlayDisplay) { - m_overlay_display = applet.get(); - } else if (is_application) { - ASSERT(m_application == nullptr); - m_application = applet.get(); + if (applet->applet_id == AppletId::QLaunch) { + ASSERT(m_home_menu == nullptr); + m_home_menu = applet.get(); + } else if (applet->applet_id == AppletId::OverlayDisplay) { + m_overlay_display = applet.get(); + } else if (is_application) { + ASSERT(m_application == nullptr); + m_application = applet.get(); + } + + this->UpdateCurrentApplicationLocked(); + + m_event_observer->TrackAppletProcess(*applet); + m_applets.emplace(applet->aruid.pid, std::move(applet)); } - m_event_observer->TrackAppletProcess(*applet); - m_applets.emplace(applet->aruid.pid, std::move(applet)); + this->NotifyApplicationChangedIfNeeded(); +} + +void WindowSystem::UpdateCurrentApplicationLocked() { + const Applet* const candidate = m_application != nullptr ? m_application : m_home_menu; + if (candidate == nullptr) { + return; + } + + auto* const process = candidate->process->GetHandle(); + if (process == nullptr || process == m_system.Kernel().ApplicationProcess()) { + return; + } + + LOG_INFO(Service_AM, "Current application is now {:016X}", candidate->program_id); + + m_system.Kernel().SetApplicationProcess(process); + Settings::SetCurrentProgramID(candidate->program_id); + + m_pending_application_notification = candidate->program_id; +} + +void WindowSystem::NotifyApplicationChangedIfNeeded() { + std::optional program_id; + { + std::scoped_lock lk{m_lock}; + program_id = std::exchange(m_pending_application_notification, std::nullopt); + } + + if (!program_id.has_value()) { + return; + } + + m_system.NotifyApplicationChanged(*program_id); } std::shared_ptr WindowSystem::GetByAppletResourceUserId(u64 aruid) { @@ -169,18 +209,40 @@ void WindowSystem::OnExitRequested() { } void WindowSystem::SendButtonAppletMessageLocked(AppletMessage message) { - if (m_home_menu) { - std::scoped_lock lk_home{m_home_menu->lock}; - m_home_menu->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message); - } - if (m_overlay_display) { - std::scoped_lock lk_overlay{m_overlay_display->lock}; - m_overlay_display->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message); - } - if (m_application) { - std::scoped_lock lk_application{m_application->lock}; - m_application->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message); - } + const auto is_blocked = [message](const Applet& applet) { + if (message == AppletMessage::DetectShortPressingHomeButton && + applet.applet_id == AppletId::OverlayDisplay && + !applet.overlay_watching_short_home_button) { + return true; + } + + switch (message) { + case AppletMessage::DetectShortPressingHomeButton: + return applet.home_button_short_pressed_blocked; + case AppletMessage::DetectLongPressingHomeButton: + return applet.home_button_long_pressed_blocked; + default: + return false; + } + }; + + const auto send_to = [&](Applet* applet) { + if (!applet) { + return; + } + std::scoped_lock lk{applet->lock}; + if (is_blocked(*applet)) { + LOG_DEBUG(Service_AM, "Applet {} is blocking message {}", + static_cast(applet->applet_id), static_cast(message)); + return; + } + applet->lifecycle_manager.PushUnorderedMessage(m_system.Kernel(), message); + }; + + send_to(m_home_menu); + send_to(m_overlay_display); + send_to(m_application); + if (m_event_observer) { m_event_observer->RequestUpdate(); } @@ -192,19 +254,9 @@ void WindowSystem::OnSystemButtonPress(SystemButtonType type) { case SystemButtonType::HomeButtonShortPressing: SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingHomeButton); break; - case SystemButtonType::HomeButtonLongPressing: { - // Toggle overlay foreground visibility on long home press - if (m_overlay_display) { - std::scoped_lock lk_overlay{m_overlay_display->lock}; - m_overlay_display->overlay_in_foreground = !m_overlay_display->overlay_in_foreground; - LOG_INFO(Service_AM, "Overlay long-press toggle: overlay_in_foreground={} window_visible={}", m_overlay_display->overlay_in_foreground, m_overlay_display->window_visible); - } + case SystemButtonType::HomeButtonLongPressing: SendButtonAppletMessageLocked(AppletMessage::DetectLongPressingHomeButton); - // Force a state update after toggling overlay - if (m_event_observer) { - m_event_observer->RequestUpdate(); - } - break; } + break; case SystemButtonType::CaptureButtonShortPressing: SendButtonAppletMessageLocked(AppletMessage::DetectShortPressingCaptureButton); break; @@ -317,6 +369,8 @@ void WindowSystem::PruneTerminatedAppletsLocked() { m_overlay_display = nullptr; } + this->UpdateCurrentApplicationLocked(); + // Finalize applet. applet->OnProcessTerminatedLocked(); @@ -389,7 +443,20 @@ void WindowSystem::TerminateChildAppletsLocked(Applet* applet) { applet->lock.lock(); } -void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking) { +bool WindowSystem::IsOverlayOpenLocked(const Applet& overlay) const { + return overlay.window_visible && overlay.overlay_watching_short_home_button; +} + +bool WindowSystem::DoesOverlayTakeInputLocked() const { + if (m_overlay_display == nullptr) { + return false; + } + + std::scoped_lock lk{m_overlay_display->lock}; + return this->IsOverlayOpenLocked(*m_overlay_display); +} + +void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input) { // With no applet, we don't have anything to do. if (!applet) { return; @@ -420,24 +487,18 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b return false; }(); + const bool is_overlay = applet->applet_id == AppletId::OverlayDisplay; + // Update visibility state. - // Overlay applets should always be visible when window_visible is true, regardless of foreground state - const bool should_be_visible = (applet->applet_id == AppletId::OverlayDisplay) - ? applet->window_visible - : (is_foreground && applet->window_visible); + const bool should_be_visible = + is_overlay ? applet->window_visible : (is_foreground && applet->window_visible); applet->display_layer_manager.SetWindowVisibility(should_be_visible); + const bool needs_hid_input = + is_overlay ? this->IsOverlayOpenLocked(*applet) + : (is_foreground && applet->window_visible && !overlay_takes_input); - const bool should_be_interactible = (applet->applet_id == AppletId::OverlayDisplay) - ? applet->overlay_in_foreground - : (is_foreground && applet->window_visible && !overlay_blocking); - - if (applet->applet_id == AppletId::OverlayDisplay || applet->applet_id == AppletId::Application) { - LOG_DEBUG(Service_AM, "UpdateAppletStateLocked: applet={} overlay_in_foreground={} is_foreground={} window_visible={} overlay_blocking={} should_be_interactible={}", - static_cast(applet->applet_id), applet->overlay_in_foreground, is_foreground, applet->window_visible, overlay_blocking, should_be_interactible); - } - - applet->SetInteractibleLocked(should_be_interactible); + applet->SetInteractibleLocked(needs_hid_input, needs_hid_input); // Update focus state and suspension. const bool is_obscured = has_obscuring_child_applets || !applet->window_visible; @@ -453,23 +514,18 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b applet->UpdateSuspensionStateLocked(true); } - // Z-index logic like in reference C# implementation (tuned for overlay extremes) - s32 z_index = 0; - const bool now_foreground = inherited_foreground; - if (applet->applet_id == AppletId::OverlayDisplay) { - z_index = applet->overlay_in_foreground ? 100000 : -1; - } else if (now_foreground && !is_obscured) { - z_index = 2; - } else if (now_foreground) { - z_index = 1; - } else { - z_index = 0; + // Layer ordering. Composition sorts back-to-front. Now with enums for calrity. + s32 z_index = Background; + if (is_overlay) { + z_index = Overlay; + } else if (inherited_foreground) { + z_index = is_obscured ? Foreground : ForegroundVisible; } applet->display_layer_manager.SetOverlayZIndex(z_index); // Recurse into child applets. for (const auto& child_applet : applet->child_applets) { - this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_blocking); + this->UpdateAppletStateLocked(child_applet.get(), is_foreground, overlay_takes_input); } } diff --git a/src/core/hle/service/am/window_system.h b/src/core/hle/service/am/window_system.h index 485e57f902..824ae14a1d 100644 --- a/src/core/hle/service/am/window_system.h +++ b/src/core/hle/service/am/window_system.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "common/common_types.h" #include "core/hle/service/am/am_types.h" @@ -60,11 +61,16 @@ public: void OnPowerButtonPressed(ButtonPressDuration type) {} private: + void UpdateCurrentApplicationLocked(); + void NotifyApplicationChangedIfNeeded(); void PruneTerminatedAppletsLocked(); bool RestartAppletProcessLocked(Applet* applet); bool LockHomeMenuIntoForegroundLocked(); void TerminateChildAppletsLocked(Applet* applet); - void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false); + bool IsOverlayOpenLocked(const Applet& overlay) const; + bool DoesOverlayTakeInputLocked() const; + + void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_takes_input); void SendButtonAppletMessageLocked(AppletMessage message); private: @@ -88,6 +94,7 @@ private: // Applet map by aruid. std::map> m_applets{}; + std::optional m_pending_application_notification{}; }; } // namespace Service::AM diff --git a/src/core/hle/service/aoc/addon_content_manager.cpp b/src/core/hle/service/aoc/addon_content_manager.cpp index 4d3e640bb3..be2aa34baa 100644 --- a/src/core/hle/service/aoc/addon_content_manager.cpp +++ b/src/core/hle/service/aoc/addon_content_manager.cpp @@ -23,6 +23,7 @@ #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/server_manager.h" +#include "core/hle/service/am/am_results.h" #include "core/loader/loader.h" namespace Service::AOC { @@ -31,6 +32,10 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { return FileSys::GetBaseTitleID(title_id) == base; } +static u64 GetCallerBaseTitleID(Core::System& system, const ClientProcessId& process_id) { + return FileSys::GetBaseTitleID(system.ResolveCallerProgramId(*process_id)); +} + static std::vector AccumulateAOCTitleIDs(Core::System& system) { std::vector add_on_content; const auto& rcu = system.GetContentProvider(); @@ -91,7 +96,7 @@ IAddOnContentManager::~IAddOnContentManager() { Result IAddOnContentManager::CountAddOnContent(Out out_count, ClientProcessId process_id) { LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid); - const auto current = system.GetApplicationProcessProgramID(); + const auto current = GetCallerBaseTitleID(system, process_id); const auto& disabled = Settings::values.disabled_addons[current]; if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) { @@ -112,7 +117,7 @@ Result IAddOnContentManager::ListAddOnContent(Out out_count, LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count, process_id.pid); - const auto current = FileSys::GetBaseTitleID(system.GetApplicationProcessProgramID()); + const auto current = GetCallerBaseTitleID(system, process_id); std::vector out; const auto& disabled = Settings::values.disabled_addons[current]; @@ -126,8 +131,7 @@ Result IAddOnContentManager::ListAddOnContent(Out out_count, } } - // TODO(DarkLordZach): Find the correct error code. - R_UNLESS(out.size() >= offset, ResultUnknown); + R_UNLESS(out.size() >= offset, AM::ResultApplicationRecordNotFound); *out_count = static_cast(std::min(out.size() - offset, count)); std::rotate(out.begin(), out.begin() + offset, out.end()); @@ -141,7 +145,7 @@ Result IAddOnContentManager::GetAddOnContentBaseId(Out out_title_id, ClientProcessId process_id) { LOG_DEBUG(Service_AOC, "called. process_id={}", process_id.pid); - const auto title_id = system.GetApplicationProcessProgramID(); + const auto title_id = system.ResolveCallerProgramId(*process_id); const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), system.GetContentProvider()}; diff --git a/src/core/hle/service/bcat/bcat_service.cpp b/src/core/hle/service/bcat/bcat_service.cpp index 210fd77094..bc2a3e3457 100644 --- a/src/core/hle/service/bcat/bcat_service.cpp +++ b/src/core/hle/service/bcat/bcat_service.cpp @@ -24,8 +24,8 @@ static u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) { return out; } -IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_) - : ServiceFramework{system_, "IBcatService"}, backend{backend_}, +IBcatService::IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_) + : ServiceFramework{system_, "IBcatService"}, backend{backend_}, program_id{program_id_}, progress{{ ProgressServiceBackend{system_, "Normal"}, ProgressServiceBackend{system_, "Directory"}, @@ -70,8 +70,7 @@ Result IBcatService::RequestSyncDeliveryCache( LOG_DEBUG(Service_BCAT, "called"); auto& progress_backend{GetProgressBackend(SyncType::Normal)}; - backend.Synchronize(system.Kernel(), {system.GetApplicationProcessProgramID(), - GetCurrentBuildID(system.GetApplicationProcessBuildID())}, + backend.Synchronize(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())}, GetProgressBackend(SyncType::Normal)); *out_interface = std::make_shared( @@ -86,9 +85,8 @@ Result IBcatService::RequestSyncDeliveryCacheWithDirectoryName( LOG_DEBUG(Service_BCAT, "called, name={}", name); auto& progress_backend{GetProgressBackend(SyncType::Directory)}; - backend.SynchronizeDirectory(system.Kernel(), {system.GetApplicationProcessProgramID(), - GetCurrentBuildID(system.GetApplicationProcessBuildID())}, - name, progress_backend); + backend.SynchronizeDirectory(system.Kernel(), {program_id, GetCurrentBuildID(system.GetApplicationProcessBuildID())}, + name, progress_backend); *out_interface = std::make_shared( system, progress_backend.GetEvent(), progress_backend.GetImpl()); diff --git a/src/core/hle/service/bcat/bcat_service.h b/src/core/hle/service/bcat/bcat_service.h index dda5a2d5f3..5e11059a78 100644 --- a/src/core/hle/service/bcat/bcat_service.h +++ b/src/core/hle/service/bcat/bcat_service.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -19,7 +22,7 @@ class IDeliveryCacheProgressService; class IBcatService final : public ServiceFramework { public: - explicit IBcatService(Core::System& system_, BcatBackend& backend_); + explicit IBcatService(Core::System& system_, BcatBackend& backend_, u64 program_id_); ~IBcatService() override; private: @@ -39,6 +42,7 @@ private: const ProgressServiceBackend& GetProgressBackend(SyncType type) const; BcatBackend& backend; + u64 program_id; std::array(SyncType::Count)> progress; }; diff --git a/src/core/hle/service/bcat/service_creator.cpp b/src/core/hle/service/bcat/service_creator.cpp index ca339e5a68..15f59d3056 100644 --- a/src/core/hle/service/bcat/service_creator.cpp +++ b/src/core/hle/service/bcat/service_creator.cpp @@ -1,6 +1,10 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/core.h" #include "core/hle/service/bcat/bcat_service.h" #include "core/hle/service/bcat/delivery_cache_storage_service.h" #include "core/hle/service/bcat/service_creator.h" @@ -37,7 +41,8 @@ IServiceCreator::~IServiceCreator() = default; Result IServiceCreator::CreateBcatService(ClientProcessId process_id, OutInterface out_interface) { LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid); - *out_interface = std::make_shared(system, *backend); + *out_interface = + std::make_shared(system, *backend, system.ResolveCallerProgramId(*process_id)); R_SUCCEED(); } @@ -45,7 +50,7 @@ Result IServiceCreator::CreateDeliveryCacheStorageService( ClientProcessId process_id, OutInterface out_interface) { LOG_INFO(Service_BCAT, "called, process_id={}", process_id.pid); - const auto title_id = system.GetApplicationProcessProgramID(); + const auto title_id = system.ResolveCallerProgramId(*process_id); *out_interface = std::make_shared(system, fsc.GetBCATDirectory(title_id)); R_SUCCEED(); diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp index 08725cb565..407557b472 100644 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -243,10 +243,15 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, return SaveScreenShot(out_entry, attribute, report_option, {}, image_data, aruid); } -Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, AlbumReportOption report_option, const ApplicationData& app_data, std::span image_data, u64 aruid) { +Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, + const ScreenShotAttribute& attribute, + AlbumReportOption report_option, + const ApplicationData& app_data, std::span image_data, + u64 aruid) { R_UNLESS(!image_data.empty(), ResultUnknown); //TODO: ??? - const u64 title_id = system.GetApplicationProcessProgramID(); + const u64 title_id = system.ResolveCallerProgramId(aruid); + auto static_service = system.ServiceManager().GetService("time:u", true); diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index d89f147a75..3d54564612 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp @@ -95,7 +95,8 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r manager->FlipVerticallyOnWrite(invert_y); manager->SaveScreenShot(entry, attribute, report_option, image_data, {}); }, - layout); + layout, + Nvnflinger::LayerStackId::Screenshot); } } // namespace Service::Capture diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 329a18e23c..9689bce8dd 100644 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -1164,7 +1164,7 @@ Result IHidServer::InitializeSevenSixAxisSensor(ClientAppletResourceUserId aruid GetResourceManager()->GetConsoleSixAxis()->Activate(); GetResourceManager()->GetSevenSixAxis()->Activate(); - GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress()); + GetResourceManager()->GetSevenSixAxis()->SetTransferMemoryAddress(t_mem_1->GetSourceAddress(), t_mem_1->GetOwner()); R_SUCCEED(); } diff --git a/src/core/hle/service/hid/hidbus.cpp b/src/core/hle/service/hid/hidbus.cpp index dff5df47f4..70bb5437e3 100644 --- a/src/core/hle/service/hid/hidbus.cpp +++ b/src/core/hle/service/hid/hidbus.cpp @@ -312,7 +312,7 @@ Result Hidbus::EnableJoyPollingReceiveMode(u32 t_mem_size, JoyPollingMode pollin auto& device = devices[device_index.value()].device; device->SetPollingMode(polling_mode); - device->SetTransferMemoryAddress(t_mem->GetSourceAddress()); + device->SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner()); R_SUCCEED(); } diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 7d7368ff9c..3d87e6788d 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -147,7 +150,7 @@ Result IRS::RunImageTransferProcessor( MakeProcessorWithCoreContext(camera_handle, device); auto& image_transfer_processor = GetProcessor(camera_handle); image_transfer_processor.SetConfig(processor_config); - image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); + image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(), t_mem->GetOwner()); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::IR); @@ -295,7 +298,8 @@ Result IRS::RunImageTransferExProcessor( MakeProcessorWithCoreContext(camera_handle, device); auto& image_transfer_processor = GetProcessor(camera_handle); image_transfer_processor.SetConfig(processor_config); - image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); + image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress(), + t_mem->GetOwner()); npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, Common::Input::PollingMode::IR); diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp index 626b643c00..ec3a3a8dcb 100644 --- a/src/core/hle/service/jit/jit.cpp +++ b/src/core/hle/service/jit/jit.cpp @@ -35,7 +35,7 @@ public: , process{kernel, process_} , user_rx{std::move(user_rx_)} , user_ro{std::move(user_ro_)} - , context{system_.ApplicationMemory()} + , context{process_->GetMemory()} { // clang-format off diff --git a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp index 6e486855d6..e8776d64b1 100644 --- a/src/core/hle/service/ns/read_only_application_control_data_interface.cpp +++ b/src/core/hle/service/ns/read_only_application_control_data_interface.cpp @@ -14,6 +14,7 @@ #include "core/file_sys/control_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/vfs/vfs.h" +#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ns/language.h" @@ -332,8 +333,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo constexpr s32 data_offset = 0; - if (t_mem != nullptr && app_count > 0) { - auto& memory = system.ApplicationMemory(); + if (t_mem != nullptr && t_mem->GetOwner() != nullptr && app_count > 0) { + auto& memory = t_mem->GetOwner()->GetMemory(); const auto t_mem_address = t_mem->GetSourceAddress(); for (size_t i = 0; i < app_count; ++i) { diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 6e9fe9ec07..b8a8979315 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -77,6 +77,7 @@ void nvdisp_disp0::Composite(std::span sorted_layers .transform_flags = layer.transform, .crop_rect = layer.crop_rect, .blending = ConvertBlending(layer.blending), + .layer_stack_mask = layer.layer_stack_mask, }); for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index a9f14df612..358679b831 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -69,7 +69,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span inpu case 0x3: return WrapFixed(this, &nvhost_gpu::ChannelSetTimeout, input, output); case 0x8: - return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, false); + return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, false); case 0x9: return WrapFixed(this, &nvhost_gpu::AllocateObjectContext, input, output); case 0xb: @@ -83,7 +83,7 @@ NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, std::span inpu case 0x1a: return WrapFixed(this, &nvhost_gpu::AllocGPFIFOEx2, input, output, fd); case 0x1b: - return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, true); + return WrapFixedVariable(this, &nvhost_gpu::SubmitGPFIFOBase1, input, output, fd, true); case 0x1d: return WrapFixed(this, &nvhost_gpu::ChannelSetTimeslice, input, output); default: @@ -387,8 +387,19 @@ NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandL return NvResult::Success; } +Core::Memory::Memory& nvhost_gpu::GetSessionMemory(DeviceFD fd) { + if (const auto it = sessions.find(fd); it != sessions.end()) + if (auto* const session = core.GetSession(it->second); + session != nullptr && session->process != nullptr) + return session->process->GetMemory(); + + LOG_ERROR(Service_NVDRV, "No session for fd={}, falling back to application memory", fd); + return system.ApplicationMemory(); +} + NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, - std::span commands, bool kickoff) { + std::span commands, DeviceFD fd, + bool kickoff) { if (params.num_entries > commands.size()) { UNIMPLEMENTED(); return NvResult::InvalidSize; @@ -396,7 +407,7 @@ NvResult nvhost_gpu::SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, Tegra::CommandList entries(params.num_entries); if (kickoff) { - system.ApplicationMemory().ReadBlock(params.address, entries.command_lists.data(), + this->GetSessionMemory(fd).ReadBlock(params.address, entries.command_lists.data(), params.num_entries * sizeof(Tegra::CommandListHeader)); } else { std::memcpy(entries.command_lists.data(), commands.data(), diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index c29231dc0c..c49aaa54a2 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -16,6 +16,10 @@ #include "core/hle/service/nvdrv/nvdata.h" #include "video_core/dma_pusher.h" +namespace Core::Memory { +class Memory; +} + namespace Tegra { namespace Control { struct ChannelState; @@ -196,8 +200,11 @@ private: NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, Tegra::CommandList&& entries); + Core::Memory::Memory& GetSessionMemory(DeviceFD fd); + NvResult SubmitGPFIFOBase1(IoctlSubmitGpfifo& params, - std::span commands, bool kickoff = false); + std::span commands, DeviceFD fd, + bool kickoff = false); NvResult SubmitGPFIFOBase2(IoctlSubmitGpfifo& params, std::span commands); diff --git a/src/core/hle/service/nvnflinger/display.h b/src/core/hle/service/nvnflinger/display.h index ada302a79e..0beb52b966 100644 --- a/src/core/hle/service/nvnflinger/display.h +++ b/src/core/hle/service/nvnflinger/display.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -15,7 +15,8 @@ struct Layer { explicit Layer(std::shared_ptr buffer_item_consumer_, s32 consumer_id_) : buffer_item_consumer(std::move(buffer_item_consumer_)), consumer_id(consumer_id_), - blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false) {} + blending(LayerBlending::None), visible(true), z_index(0), is_overlay(false), + layer_stack_mask(DefaultLayerStackMask) {} ~Layer() { buffer_item_consumer->Abandon(); } @@ -26,6 +27,7 @@ struct Layer { bool visible; s32 z_index; bool is_overlay; + u32 layer_stack_mask; }; struct LayerStack { diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp index e491b24a8f..5decb791e1 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.cpp +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp @@ -115,6 +115,7 @@ u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, Display& display, .transform = static_cast(item.transform), .crop_rect = item.crop, .acquire_fence = item.fence, + .layer_stack_mask = layer->layer_stack_mask, }); } diff --git a/src/core/hle/service/nvnflinger/hwc_layer.h b/src/core/hle/service/nvnflinger/hwc_layer.h index f71a5d8227..fd9819b526 100644 --- a/src/core/hle/service/nvnflinger/hwc_layer.h +++ b/src/core/hle/service/nvnflinger/hwc_layer.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -23,6 +26,25 @@ enum class LayerBlending : u32 { Coverage = 0x405, }; +enum class LayerStackId : u32 { + Default = 0, + Lcd = 1, + Screenshot = 2, + Recording = 3, + LastFrame = 4, + Arbitrary = 5, + ApplicationForDebug = 6, + Null = 10, +}; + +constexpr u32 LayerStackBit(LayerStackId id) { + return 1U << static_cast(id); +} + +constexpr u32 DefaultLayerStackMask = + LayerStackBit(LayerStackId::Default) | LayerStackBit(LayerStackId::Screenshot) | + LayerStackBit(LayerStackId::Recording) | LayerStackBit(LayerStackId::LastFrame); + struct HwcLayer { u32 buffer_handle; u32 offset; @@ -35,6 +57,7 @@ struct HwcLayer { android::BufferTransformFlags transform; Common::Rectangle crop_rect; android::Fence acquire_fence; + u32 layer_stack_mask; }; } // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/surface_flinger.cpp b/src/core/hle/service/nvnflinger/surface_flinger.cpp index fbf70592bd..a6136c111d 100644 --- a/src/core/hle/service/nvnflinger/surface_flinger.cpp +++ b/src/core/hle/service/nvnflinger/surface_flinger.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -104,6 +104,14 @@ void SurfaceFlinger::SetLayerBlending(s32 consumer_binder_id, LayerBlending blen } } +void SurfaceFlinger::SetLayerStackMask(s32 consumer_binder_id, u32 layer_stack_mask) { + if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) { + layer->layer_stack_mask = layer_stack_mask; + LOG_DEBUG(Service_VI, "Layer {} stack mask set to {:#x}", consumer_binder_id, + layer_stack_mask); + } +} + void SurfaceFlinger::SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay) { if (const auto layer = this->FindLayer(consumer_binder_id); layer != nullptr) { layer->is_overlay = is_overlay; diff --git a/src/core/hle/service/nvnflinger/surface_flinger.h b/src/core/hle/service/nvnflinger/surface_flinger.h index 05b4206c34..5eb0b22ca9 100644 --- a/src/core/hle/service/nvnflinger/surface_flinger.h +++ b/src/core/hle/service/nvnflinger/surface_flinger.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -48,6 +48,7 @@ public: void SetLayerVisibility(s32 consumer_binder_id, bool visible); void SetLayerBlending(s32 consumer_binder_id, LayerBlending blending); void SetLayerIsOverlay(s32 consumer_binder_id, bool is_overlay); + void SetLayerStackMask(s32 consumer_binder_id, u32 layer_stack_mask); std::shared_ptr FindLayer(s32 consumer_binder_id); diff --git a/src/core/hle/service/pctl/parental_control_service.cpp b/src/core/hle/service/pctl/parental_control_service.cpp index c2ddbcbc51..97c178799e 100644 --- a/src/core/hle/service/pctl/parental_control_service.cpp +++ b/src/core/hle/service/pctl/parental_control_service.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -13,8 +13,10 @@ namespace Service::PCTL { -IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_) +IParentalControlService::IParentalControlService(Core::System& system_, Capability capability_, + u64 program_id_) : ServiceFramework{system_, "IParentalControlService"}, capability{capability_}, + program_id{program_id_}, service_context{system_, "IParentalControlService"}, synchronization_event{service_context}, unlinked_event{service_context}, request_suspension_event{service_context} { // clang-format off @@ -202,7 +204,6 @@ Result IParentalControlService::Initialize() { // TODO(ogniK): Recovery flag initialization for pctl:r - const auto program_id = system.GetApplicationProcessProgramID(); if (program_id != 0) { const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), system.GetContentProvider()}; diff --git a/src/core/hle/service/pctl/parental_control_service.h b/src/core/hle/service/pctl/parental_control_service.h index 58e3b6123f..92d1d38429 100644 --- a/src/core/hle/service/pctl/parental_control_service.h +++ b/src/core/hle/service/pctl/parental_control_service.h @@ -16,7 +16,8 @@ namespace Service::PCTL { class IParentalControlService final : public ServiceFramework { public: - explicit IParentalControlService(Core::System& system_, Capability capability_); + explicit IParentalControlService(Core::System& system_, Capability capability_, + u64 program_id_); ~IParentalControlService() override; private: @@ -84,6 +85,7 @@ private: RestrictionSettings restriction_settings{}; std::array pin_code{}; Capability capability{}; + u64 program_id{}; // TODO: this is raw PlayTimerSettings raw_play_timer_settings{}; diff --git a/src/core/hle/service/pctl/parental_control_service_factory.cpp b/src/core/hle/service/pctl/parental_control_service_factory.cpp index 7d8f361e95..ae278a9bbc 100644 --- a/src/core/hle/service/pctl/parental_control_service_factory.cpp +++ b/src/core/hle/service/pctl/parental_control_service_factory.cpp @@ -1,6 +1,10 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/core.h" #include "core/hle/service/cmif_serialization.h" #include "core/hle/service/pctl/parental_control_service.h" #include "core/hle/service/pctl/parental_control_service_factory.h" @@ -23,17 +27,17 @@ IParentalControlServiceFactory::~IParentalControlServiceFactory() = default; Result IParentalControlServiceFactory::CreateService( Out> out_service, ClientProcessId process_id) { - LOG_DEBUG(Service_PCTL, "called"); - // TODO(ogniK): Get application id from process - *out_service = std::make_shared(system, capability); + LOG_DEBUG(Service_PCTL, "called, process_id={}", process_id.pid); + *out_service = std::make_shared( + system, capability, system.ResolveCallerProgramId(*process_id)); R_SUCCEED(); } Result IParentalControlServiceFactory::CreateServiceWithoutInitialize( Out> out_service, ClientProcessId process_id) { - LOG_DEBUG(Service_PCTL, "called"); - // TODO(ogniK): Get application id from process - *out_service = std::make_shared(system, capability); + LOG_DEBUG(Service_PCTL, "called, process_id={}", process_id.pid); + *out_service = std::make_shared( + system, capability, system.ResolveCallerProgramId(*process_id)); R_SUCCEED(); } diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp index 3e192bd840..97efb1a361 100644 --- a/src/core/hle/service/pm/pm.cpp +++ b/src/core/hle/service/pm/pm.cpp @@ -132,8 +132,7 @@ private: LOG_WARNING(Service_PM, "(Partial Implementation) called, pid={:016X}", pid); - auto list = kernel.GetProcessList(); - auto process = SearchProcessList(system.Kernel(), list, [pid](auto& p) { return p->GetProcessId() == pid; }); + auto process = kernel.GetProcessByProcessId(pid); if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; @@ -186,9 +185,7 @@ private: LOG_DEBUG(Service_PM, "called, process_id={:016X}", process_id); - auto list = kernel.GetProcessList(); - auto process = SearchProcessList(system.Kernel(), - list, [process_id](auto& p) { return p->GetProcessId() == process_id; }); + auto process = kernel.GetProcessByProcessId(process_id); if (process.IsNull()) { IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index 02fa82047d..e76c3ea93c 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -76,7 +76,7 @@ private: Type, process_id, data1.size(), data2.size()); const auto& reporter{system.GetReporter()}; - reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2}, + reporter.SavePlayReport(Type, system.ResolveCallerProgramId(process_id), {data1, data2}, process_id); IPC::ResponseBuilder rb{ctx, 2}; @@ -98,7 +98,7 @@ private: Type, user_id[1], user_id[0], process_id, data1.size(), data2.size()); const auto& reporter{system.GetReporter()}; - reporter.SavePlayReport(Type, system.GetApplicationProcessProgramID(), {data1, data2}, + reporter.SavePlayReport(Type, system.ResolveCallerProgramId(process_id), {data1, data2}, process_id, user_id); IPC::ResponseBuilder rb{ctx, 2}; diff --git a/src/core/hle/service/vi/container.cpp b/src/core/hle/service/vi/container.cpp index e7255c2b0a..9c45f6435b 100644 --- a/src/core/hle/service/vi/container.cpp +++ b/src/core/hle/service/vi/container.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -166,6 +166,16 @@ Result Container::GetLayerZIndex(u64 layer_id, s32* out_z_index) { R_RETURN(VI::ResultNotFound); } +Result Container::SetLayerStackMask(u64 layer_id, u32 layer_stack_mask) { + std::scoped_lock lk{m_lock}; + + auto* const layer = m_layers.GetLayerById(layer_id); + R_UNLESS(layer != nullptr, VI::ResultNotFound); + + m_surface_flinger->SetLayerStackMask(layer->GetConsumerBinderId(), layer_stack_mask); + R_SUCCEED(); +} + Result Container::SetLayerIsOverlay(u64 layer_id, bool is_overlay) { std::scoped_lock lk{m_lock}; diff --git a/src/core/hle/service/vi/container.h b/src/core/hle/service/vi/container.h index 5d111e71ab..e4453a6f72 100644 --- a/src/core/hle/service/vi/container.h +++ b/src/core/hle/service/vi/container.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project @@ -68,6 +68,7 @@ public: Result SetLayerZIndex(u64 layer_id, s32 z_index); Result GetLayerZIndex(u64 layer_id, s32* out_z_index); Result SetLayerIsOverlay(u64 layer_id, bool is_overlay); + Result SetLayerStackMask(u64 layer_id, u32 layer_stack_mask); void LinkVsyncEvent(u64 display_id, Event* event); void UnlinkVsyncEvent(u64 display_id, Event* event); diff --git a/src/core/hle/service/vi/shared_buffer_manager.cpp b/src/core/hle/service/vi/shared_buffer_manager.cpp index 21e42e2094..483c1f71ba 100644 --- a/src/core/hle/service/vi/shared_buffer_manager.cpp +++ b/src/core/hle/service/vi/shared_buffer_manager.cpp @@ -4,9 +4,15 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include #include +#include "common/assert.h" +#include "common/logging.h" +#include "common/scratch_buffer.h" #include "core/core.h" +#include "core/hle/kernel/k_page_group.h" #include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_system_resource.h" #include "core/hle/service/nvdrv/devices/nvmap.h" @@ -47,7 +53,7 @@ Result AllocateSharedBufferMemory(std::unique_ptr* out_page_ u32* end = system.DeviceMemory().GetPointer(block.GetAddress() + block.GetSize()); for (; start < end; start++) { - *start = 0xFF0000FF; + *start = 0x00000000; } } @@ -168,7 +174,14 @@ constexpr u32 SharedBufferBlockLinearWidth = 1280; constexpr u32 SharedBufferBlockLinearHeight = 768; constexpr u32 SharedBufferBlockLinearStride = SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp; -constexpr u32 SharedBufferNumSlots = 7; + +constexpr u32 SharedBufferNumCaptureSlots = 3; +constexpr u32 SharedBufferSlotsPerSession = 2; +constexpr u32 SharedBufferMaxSessions = 2; + +constexpr u32 SharedBufferNumSlots = + SharedBufferNumCaptureSlots + SharedBufferSlotsPerSession * SharedBufferMaxSessions; +static_assert(SharedBufferNumSlots <= 16, "Shared buffer pool exceeds the maximum texture count"); constexpr u32 SharedBufferWidth = 1280; constexpr u32 SharedBufferHeight = 720; @@ -192,7 +205,52 @@ constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] { return layout; }(); -void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) { +constexpr u32 GetCaptureSlot(CaptureKind kind) { + return static_cast(kind); +} +static_assert(static_cast(CaptureKind::CallerApplet) + 1 == SharedBufferNumCaptureSlots, + "Capture slot count does not match CaptureKind"); + +constexpr u32 GetPresentationSlot(u32 slot_base, u32 index) { + return SharedBufferNumCaptureSlots + slot_base + index; +} + +constexpr u32 ColorOpaqueBlackRgba32 = 0xFF000000; + +template +void ForEachPoolChunk(Core::System& system, Kernel::KPageGroup& page_group, u64 offset, u64 size, + F&& writer) { + Common::ScratchBuffer scratch; + const u64 range_end = offset + size; + u64 pool_pos = 0; + + for (auto& block : page_group) { + const u64 block_begin = pool_pos; + const u64 block_end = block_begin + block.GetSize(); + pool_pos = block_end; + + if (block_end <= offset) { + continue; + } + if (block_begin >= range_end) { + break; + } + + const u64 chunk_begin = (std::max)(block_begin, offset); + const u64 chunk_end = (std::min)(block_end, range_end); + const u64 chunk_size = chunk_end - chunk_begin; + + u8* const dst = + system.DeviceMemory().GetPointer(block.GetAddress()) + (chunk_begin - block_begin); + + writer(dst, chunk_begin - offset, chunk_size); + + system.GPU().Host1x().MemoryManager().ApplyOpOnPointer( + dst, scratch, [&](DAddr addr) { system.GPU().InvalidateRegion(addr, chunk_size); }); + } +} + +void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 producer_slot, u32 pool_slot, u32 handle) { auto buffer = std::make_shared(); buffer->width = SharedBufferWidth; buffer->height = SharedBufferHeight; @@ -200,8 +258,8 @@ void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 han buffer->format = SharedBufferBlockLinearFormat; buffer->external_format = SharedBufferBlockLinearFormat; buffer->buffer_id = handle; - buffer->offset = slot * SharedBufferSlotSize; - ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError); + buffer->offset = pool_slot * SharedBufferSlotSize; + ASSERT(producer.SetPreallocatedBuffer(producer_slot, buffer) == android::Status::NoError); } } // namespace @@ -215,59 +273,91 @@ SharedBufferManager::~SharedBufferManager() = default; Result SharedBufferManager::CreateSession(Kernel::KProcess* owner_process, u64* out_buffer_id, u64* out_layer_handle, u64 display_id, bool enable_blending) { - std::scoped_lock lk{m_guard}; + { + std::scoped_lock lk{m_guard}; - // Ensure we haven't already created. - const u64 aruid = owner_process->GetProcessId(); - R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied); + // Ensure we haven't already created. + const u64 aruid = owner_process->GetProcessId(); + R_UNLESS(!m_sessions.contains(aruid), VI::ResultPermissionDenied); - // Allocate memory for the shared buffer if needed. - if (!m_buffer_page_group) { - R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system, - SharedBufferSize)); + // Allocate memory for the shared buffer if needed. + if (!m_buffer_page_group) { + R_TRY(AllocateSharedBufferMemory(std::addressof(m_buffer_page_group), m_system, + SharedBufferSize)); - // Record buffer id. - m_buffer_id = m_next_buffer_id++; + // Record buffer id. + m_buffer_id = m_next_buffer_id++; - // Record display id. - m_display_id = display_id; + // Record display id. + m_display_id = display_id; + + for (u32 slot = 0; slot < SharedBufferNumCaptureSlots; slot++) { + ForEachPoolChunk(m_system, *m_buffer_page_group, u64{slot} * SharedBufferSlotSize, + SharedBufferSlotSize, [](u8* dst, u64, u64 length) { + std::fill_n(reinterpret_cast(dst), length / sizeof(u32), + ColorOpaqueBlackRgba32); + }); + } + } + + // Claim a presentation slot range. + u32 slot_base = 0; + + std::array in_use{}; + for (const auto& [existing_aruid, existing] : m_sessions) { + const u32 index = existing.presentation_slot_base / SharedBufferSlotsPerSession; + + if (index < in_use.size()) + in_use[index] = true; + } + + u32 index = 0; + while (index < in_use.size() && in_use[index]) + index++; + + if (index >= in_use.size()) { + LOG_ERROR(Service_VI, "Out of shared buffer presentation slots ({} sessions)", SharedBufferMaxSessions); + R_THROW(VI::ResultOperationFailed); + } + + slot_base = index * SharedBufferSlotsPerSession; + + // Map into process. + Common::ProcessAddress map_address{}; + R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group, + owner_process, m_system)); + + // Create new session. + auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{}); + auto& session = it->second; + session.presentation_slot_base = slot_base; + + auto& container = m_nvdrv->GetContainer(); + session.session_id = container.OpenSession(owner_process); + session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id); + + // Create an nvmap handle for the buffer and assign the memory to it. + R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv, + session.nvmap_fd, map_address, SharedBufferSize)); + + // Create and open a layer for the display. + s32 producer_binder_id; + R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id), + std::addressof(session.layer_id), display_id)); + + // Configure blending and z-index + R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending)); + + // Get the producer and set preallocated buffers. + std::shared_ptr producer; + R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id)); + for (u32 i = 0; i < SharedBufferSlotsPerSession; i++) + MakeGraphicBuffer(*producer, i, GetPresentationSlot(session.presentation_slot_base, i), session.buffer_nvmap_handle); + + // Assign outputs. + *out_buffer_id = m_buffer_id; + *out_layer_handle = session.layer_id; } - - // Map into process. - Common::ProcessAddress map_address{}; - R_TRY(MapSharedBufferIntoProcessAddressSpace(std::addressof(map_address), m_buffer_page_group, - owner_process, m_system)); - - // Create new session. - auto [it, was_emplaced] = m_sessions.emplace(aruid, SharedBufferSession{}); - auto& session = it->second; - - auto& container = m_nvdrv->GetContainer(); - session.session_id = container.OpenSession(owner_process); - session.nvmap_fd = m_nvdrv->Open("/dev/nvmap", session.session_id); - - // Create an nvmap handle for the buffer and assign the memory to it. - R_TRY(AllocateHandleForBuffer(std::addressof(session.buffer_nvmap_handle), *m_nvdrv, - session.nvmap_fd, map_address, SharedBufferSize)); - - // Create and open a layer for the display. - s32 producer_binder_id; - R_TRY(m_container.CreateStrayLayer(std::addressof(producer_binder_id), - std::addressof(session.layer_id), display_id)); - - // Configure blending and z-index - R_ASSERT(m_container.SetLayerBlending(session.layer_id, enable_blending)); - - // Get the producer and set preallocated buffers. - std::shared_ptr producer; - R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), session.layer_id)); - MakeGraphicBuffer(*producer, 0, session.buffer_nvmap_handle); - MakeGraphicBuffer(*producer, 1, session.buffer_nvmap_handle); - - // Assign outputs. - *out_buffer_id = m_buffer_id; - *out_layer_handle = session.layer_id; - // We succeeded. R_SUCCEED(); } @@ -336,14 +426,51 @@ Result SharedBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence, SharedBufferBlockLinearFormat, 0) == android::Status::NoError, VI::ResultOperationFailed); - // Assign remaining outputs. - *out_target_slot = slot; - out_slot_indexes = {0, 1, -1, -1}; + out_slot_indexes.fill(-1); + + { + std::scoped_lock lk{m_guard}; + const auto* const session = this->FindSessionByLayerIdLocked(layer_id); + if (session == nullptr) { + producer->CancelBuffer(slot, *out_fence); + // LOG_DEBUG(Service_VI, "No Session found"); + R_THROW(VI::ResultNotFound); + } + + for (u32 i = 0; i < SharedBufferSlotsPerSession; i++) + out_slot_indexes[i] = + static_cast(GetPresentationSlot(session->presentation_slot_base, i)); + + *out_target_slot = static_cast( + GetPresentationSlot(session->presentation_slot_base, static_cast(slot))); + } // We succeeded. R_SUCCEED(); } +Result SharedBufferManager::GetProducerSlotLocked(s32* out_producer_slot, u64 layer_id, + s64 pool_slot) const { + const auto* const session = this->FindSessionByLayerIdLocked(layer_id); + R_UNLESS(session != nullptr, VI::ResultNotFound); + + const s64 base = GetPresentationSlot(session->presentation_slot_base, 0); + const s64 producer_slot = pool_slot - base; + R_UNLESS(producer_slot >= 0 && producer_slot < SharedBufferSlotsPerSession, + VI::ResultOperationFailed); + + *out_producer_slot = static_cast(producer_slot); + R_SUCCEED(); +} + +const SharedBufferSession* SharedBufferManager::FindSessionByLayerIdLocked(u64 layer_id) const { + for (const auto& [aruid, session] : m_sessions) + if (session.layer_id == layer_id) + return std::addressof(session); + + return nullptr; +} + Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle crop_region, u32 transform, s32 swap_interval, u64 layer_id, @@ -352,14 +479,20 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence, std::shared_ptr producer; R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id)); + s32 producer_slot; + { + std::scoped_lock lk{m_guard}; + R_TRY(this->GetProducerSlotLocked(std::addressof(producer_slot), layer_id, slot)); + } + // Request to queue the buffer. std::shared_ptr buffer; - R_UNLESS(producer->RequestBuffer(static_cast(slot), std::addressof(buffer)) == + R_UNLESS(producer->RequestBuffer(producer_slot, std::addressof(buffer)) == android::Status::NoError, VI::ResultOperationFailed); ON_RESULT_FAILURE { - producer->CancelBuffer(static_cast(slot), fence); + producer->CancelBuffer(producer_slot, fence); }; // Queue the buffer to the producer. @@ -369,12 +502,10 @@ Result SharedBufferManager::PresentSharedFrameBuffer(android::Fence fence, input.fence = fence; input.transform = static_cast(transform); input.swap_interval = swap_interval; - R_UNLESS(producer->QueueBuffer(static_cast(slot), input, std::addressof(output)) == + R_UNLESS(producer->QueueBuffer(producer_slot, input, std::addressof(output)) == android::Status::NoError, VI::ResultOperationFailed); - (void)m_container.SetLayerZIndex(layer_id, 100000); - // We succeeded. R_SUCCEED(); } @@ -384,8 +515,14 @@ Result SharedBufferManager::CancelSharedFrameBuffer(u64 layer_id, s64 slot) { std::shared_ptr producer; R_TRY(m_container.GetLayerProducerHandle(std::addressof(producer), layer_id)); + s32 producer_slot; + { + std::scoped_lock lk{m_guard}; + R_TRY(this->GetProducerSlotLocked(std::addressof(producer_slot), layer_id, slot)); + } + // Cancel. - producer->CancelBuffer(static_cast(slot), android::Fence::NoFence()); + producer->CancelBuffer(producer_slot, android::Fence::NoFence()); // We succeeded. R_SUCCEED(); @@ -404,31 +541,47 @@ Result SharedBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadabl R_SUCCEED(); } -Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index) { - std::vector capture_buffer(m_system.GPU().GetAppletCaptureBuffer()); - Common::ScratchBuffer scratch; +Result SharedBufferManager::WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index, CaptureKind kind) { + std::scoped_lock lk{m_guard}; + R_UNLESS(m_buffer_page_group != nullptr, VI::ResultNotFound); - // TODO: this could be optimized - s64 e = -1280 * 768 * 4; - for (auto& block : *m_buffer_page_group) { - u8* start = m_system.DeviceMemory().GetPointer(block.GetAddress()); - u8* end = m_system.DeviceMemory().GetPointer(block.GetAddress() + block.GetSize()); + const std::vector capture = m_system.GPU().GetAppletCaptureBuffer(); + const u32 slot = GetCaptureSlot(kind); - for (; start < end; start++) { - *start = 0; - if (e >= 0 && e < static_cast(capture_buffer.size())) { - *start = capture_buffer[e]; - } - e++; - } - - m_system.GPU().Host1x().MemoryManager().ApplyOpOnPointer(start, scratch, [&](DAddr addr) { - m_system.GPU().InvalidateRegion(addr, end - start); - }); + if (capture.size() < SharedBufferSlotSize) { + //LOG_WARNING(Service_VI, "Capture buffer is {} bytes, expected at least {}; not writing", + // capture.size(), SharedBufferSlotSize); + *out_was_written = false; + *out_layer_index = static_cast(slot); + R_SUCCEED(); } + ForEachPoolChunk(m_system, *m_buffer_page_group, u64{slot} * SharedBufferSlotSize, + SharedBufferSlotSize, + [&](u8* dst, u64 src_offset, u64 length) { + std::memcpy(dst, capture.data() + src_offset, length); + }); + *out_was_written = true; - *out_layer_index = 1; + *out_layer_index = static_cast(slot); + R_SUCCEED(); +} + +Result SharedBufferManager::ClearAppletCaptureBuffer(s32 layer_index, u32 color) { + std::scoped_lock lk{m_guard}; + R_UNLESS(m_buffer_page_group != nullptr, VI::ResultNotFound); + + if (layer_index < 0 || layer_index >= static_cast(SharedBufferNumCaptureSlots)) { + LOG_WARNING(Service_VI, "Couldnt clear non-capture slot {}", layer_index); + R_SUCCEED(); + } + + ForEachPoolChunk(m_system, *m_buffer_page_group, u64{static_cast(layer_index)} * SharedBufferSlotSize, + SharedBufferSlotSize, [&](u8* dst, u64 src_offset, u64 length) { + ASSERT(src_offset % sizeof(u32) == 0 && length % sizeof(u32) == 0); + std::fill_n(reinterpret_cast(dst), length / sizeof(u32), color); + }); + R_SUCCEED(); } diff --git a/src/core/hle/service/vi/shared_buffer_manager.h b/src/core/hle/service/vi/shared_buffer_manager.h index 9ef6234867..807db10f31 100644 --- a/src/core/hle/service/vi/shared_buffer_manager.h +++ b/src/core/hle/service/vi/shared_buffer_manager.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project @@ -48,6 +48,12 @@ static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout h struct SharedBufferSession; +enum class CaptureKind : u32 { + LastApplication, + LastForeground, + CallerApplet, +}; + class SharedBufferManager final { public: explicit SharedBufferManager(Core::System& system, Container& container, @@ -68,9 +74,15 @@ public: Result CancelSharedFrameBuffer(u64 layer_id, s64 slot); Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id); - Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index); + Result WriteAppletCaptureBuffer(bool* out_was_written, s32* out_layer_index, CaptureKind kind); + Result ClearAppletCaptureBuffer(s32 layer_index, u32 color); private: + const SharedBufferSession* FindSessionByLayerIdLocked(u64 layer_id) const; + + /// Converts a pool slot index, which is what the guest works in, back to the buffer queues slot index + Result GetProducerSlotLocked(s32* out_producer_slot, u64 layer_id, s64 pool_slot) const; + u64 m_next_buffer_id = 1; u64 m_display_id = 0; u64 m_buffer_id = 0; @@ -89,6 +101,7 @@ struct SharedBufferSession { Nvidia::NvCore::SessionId session_id = {}; u64 layer_id = {}; u32 buffer_nvmap_handle = 0; + u32 presentation_slot_base = 0; }; } // namespace Service::VI diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 69bfa2e879..90e23ccc11 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -17,6 +17,8 @@ #include "core/core.h" #include "core/file_sys/card_image.h" #include "core/file_sys/common_funcs.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/registered_cache.h" #include "core/file_sys/submission_package.h" #include "core/hle/kernel/k_process.h" #include "core/loader/deconstructed_rom_directory.h" @@ -41,6 +43,38 @@ std::optional IdentifyFileLoader(FileSys::VirtualFile file) { return std::nullopt; } +struct IndexedProgram { + FileSys::VirtualFile file; + u64 program_id; + bool update_only; +}; + +std::optional ResolveIndexedProgram(Core::System& system, u64 program_id, + std::size_t program_index) { + if (program_index == 0 || program_id == 0) { + return std::nullopt; + } + + const auto target_id = FileSys::GetBaseTitleIDWithProgramIndex(program_id, program_index); + const auto& provider = system.GetContentProvider(); + + if (auto base = provider.GetEntryRaw(target_id, FileSys::ContentRecordType::Program)) { + LOG_INFO(Loader, "Program index {} resolved to {:016X}", program_index, target_id); + return IndexedProgram{std::move(base), target_id, false}; + } + + const auto update_id = FileSys::GetUpdateTitleID(target_id); + if (auto update = provider.GetEntryRaw(update_id, FileSys::ContentRecordType::Program)) { + LOG_INFO(Loader, "Program index {} has no base program, loading it from update {:016X}", + program_index, update_id); + return IndexedProgram{std::move(update), target_id, true}; + } + + LOG_WARNING(Loader, "No program NCA for {:016X} (index {}), falling back to the container", + target_id, program_index); + return std::nullopt; +} + std::shared_ptr OpenContainerAsNsp(FileSys::VirtualFile file, FileType type, u64 program_id = 0, std::size_t program_index = 0) { @@ -321,6 +355,11 @@ std::unique_ptr GetLoader(Core::System& system, FileSys::VirtualFile return nullptr; } + if (const auto indexed = ResolveIndexedProgram(system, program_id, program_index)) { + return std::make_unique( + indexed->file, indexed->update_only ? indexed->program_id : 0); + } + FileType type = IdentifyFile(file); const FileType filename_type = GuessFromFilename(file->GetName()); diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index b656b9fe96..2f9cd68e9c 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp @@ -12,7 +12,9 @@ #include "common/scope_exit.h" #include "core/core.h" #include "core/file_sys/content_archive.h" +#include "core/file_sys/control_metadata.h" #include "core/file_sys/nca_metadata.h" +#include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs_factory.h" #include "core/hle/kernel/k_process.h" @@ -33,8 +35,14 @@ static u32 CalculatePointerBufferSize(size_t heap_size) { } } -AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_) - : AppLoader(std::move(file_)), nca(std::make_unique(file)) {} +AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file_, u64 update_only_program_id_) + : AppLoader(std::move(file_)), + nca(std::make_unique(file, nullptr, update_only_program_id_ != 0)), + update_only_program_id(update_only_program_id_) {} + +u64 AppLoader_NCA::GetProgramId() const { + return update_only_program_id != 0 ? update_only_program_id : nca->GetTitleId(); +} AppLoader_NCA::~AppLoader_NCA() = default; @@ -110,11 +118,21 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::KProcess& process, Core::S } LOG_INFO(Loader, "Set pointer buffer size to {:#x} bytes for ProgramID {:#018x} (Heap size: {:#x})", - process.GetPointerBufferSize(), nca->GetTitleId(), heap_size); + process.GetPointerBufferSize(), GetProgramId(), heap_size); + + auto metadata = + FileSys::PatchManager::GetMetadataFromBaseOrUpdate(system, this->GetProgramId()); + if (metadata.first != nullptr) { + nacp = std::move(metadata.first); + LOG_INFO(Loader, "Control data for {:016X}: name=\"{}\" supported_languages={:08X}", + this->GetProgramId(), nacp->GetApplicationName(), nacp->GetSupportedLanguages()); + } else { + LOG_WARNING(Loader, "No control data found for program {:016X}", this->GetProgramId()); + } // Register the process in the file system controller system.GetFileSystemController().RegisterProcess( - process.GetProcessId(), nca->GetTitleId(), + process.GetProcessId(), GetProgramId(), std::make_shared(*this, system.GetContentProvider(), system.GetFileSystemController())); @@ -222,7 +240,25 @@ ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) { return ResultStatus::ErrorNotInitialized; } - out_program_id = nca->GetTitleId(); + out_program_id = GetProgramId(); + return ResultStatus::Success; +} + +ResultStatus AppLoader_NCA::ReadControlData(FileSys::NACP& out_control) { + if (nacp == nullptr) { + return ResultStatus::ErrorNoControl; + } + + out_control = *nacp; + return ResultStatus::Success; +} + +ResultStatus AppLoader_NCA::ReadTitle(std::string& out_title) { + if (nacp == nullptr) { + return ResultStatus::ErrorNoControl; + } + + out_title = nacp->GetApplicationName(); return ResultStatus::Success; } diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 96779e27fc..cf63822120 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -11,6 +14,7 @@ class System; } namespace FileSys { +class NACP; class NCA; } @@ -21,7 +25,7 @@ class AppLoader_DeconstructedRomDirectory; /// Loads an NCA file class AppLoader_NCA final : public AppLoader { public: - explicit AppLoader_NCA(FileSys::VirtualFile file_); + explicit AppLoader_NCA(FileSys::VirtualFile file_, u64 update_only_program_id = 0); ~AppLoader_NCA() override; /** @@ -43,14 +47,20 @@ public: ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadProgramId(u64& out_program_id) override; + ResultStatus ReadControlData(FileSys::NACP& out_control) override; + ResultStatus ReadTitle(std::string& out_title) override; ResultStatus ReadBanner(std::vector& buffer) override; ResultStatus ReadLogo(std::vector& buffer) override; ResultStatus ReadNSOModules(Modules& modules) override; + u64 GetProgramId() const; + private: std::unique_ptr nca; + u64 update_only_program_id{}; + std::unique_ptr nacp; std::unique_ptr directory_loader; }; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index d8b4e018f7..0a3dc5885b 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -209,7 +209,10 @@ std::optional AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: // Apply cheats if they exist and the program has a valid title ID if (pm) { - system.SetApplicationProcessBuildID(nso_header.build_id); + // TODO(Maufeat): Check if there is a better way to check + if (name == "main") + system.SetApplicationProcessBuildID(nso_header.build_id); + const auto cheats = pm->CreateCheatList(nso_header.build_id); if (!cheats.empty()) { system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size); diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 726bb428f6..cfc908b4ef 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -8,6 +8,7 @@ #include "common/common_types.h" #include "core/core.h" +#include "core/file_sys/common_funcs.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" #include "core/file_sys/nca_metadata.h" @@ -37,13 +38,13 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_, secondary_loader = std::make_unique(nsp->GetExeFS(), false); } else { const auto control_nca = - nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control); + nsp->GetNCA(FileSys::GetBaseTitleID(nsp->GetProgramTitleID()), FileSys::ContentRecordType::Control); if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) { return; } std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] { - const FileSys::PatchManager pm{nsp->GetProgramTitleID(), fsc, content_provider}; + const FileSys::PatchManager pm{FileSys::GetBaseTitleID(nsp->GetProgramTitleID()), fsc, content_provider}; return pm.ParseControlNCA(*control_nca); }(); diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 8e23a54a0d..e14147f5c9 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -51,24 +51,41 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta StandardVmCallbacks::~StandardVmCallbacks() = default; +Kernel::KProcess* StandardVmCallbacks::GetProcess() const { + if (cached_process != nullptr && cached_process_id == metadata.process_id) { + return cached_process; + } + + auto process = system.Kernel().GetProcessByProcessId(metadata.process_id); + cached_process = process.IsNull() ? nullptr : process.GetPointerUnsafe(); + cached_process_id = metadata.process_id; + return cached_process; +} + void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) { + auto* const process = this->GetProcess(); + // Return zero on invalid address - if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) { + if (process == nullptr || !IsAddressInRange(address) || + !process->GetMemory().IsValidVirtualAddress(address)) { std::memset(data, 0, size); return; } - system.ApplicationMemory().ReadBlock(address, data, size); + process->GetMemory().ReadBlock(address, data, size); } void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) { + auto* const process = this->GetProcess(); + // Skip invalid memory write address - if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) { + if (process == nullptr || !IsAddressInRange(address) || + !process->GetMemory().IsValidVirtualAddress(address)) { return; } - if (system.ApplicationMemory().WriteBlock(address, data, size)) { - Core::InvalidateInstructionCacheRange(system.ApplicationProcess(), address, size); + if (process->GetMemory().WriteBlock(address, data, size)) { + Core::InvalidateInstructionCacheRange(process, address, size); } } @@ -91,14 +108,16 @@ u64 StandardVmCallbacks::HidKeysDown() { } void StandardVmCallbacks::PauseProcess() { - if (!system.ApplicationProcess()->IsSuspended()) { - system.ApplicationProcess()->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Paused); + auto* const process = this->GetProcess(); + if (process != nullptr && !process->IsSuspended()) { + process->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Paused); } } void StandardVmCallbacks::ResumeProcess() { - if (system.ApplicationProcess()->IsSuspended()) { - system.ApplicationProcess()->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Runnable); + auto* const process = this->GetProcess(); + if (process != nullptr && process->IsSuspended()) { + process->SetActivity(system.Kernel(), Kernel::Svc::ProcessActivity::Runnable); } } diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index f52f2be7c3..abd3abc93f 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -15,6 +18,10 @@ namespace Core { class System; } +namespace Kernel { +class KProcess; +} + namespace Core::Timing { class CoreTiming; struct EventType; @@ -38,8 +45,12 @@ public: private: bool IsAddressInRange(VAddr address) const; + Kernel::KProcess* GetProcess() const; + const CheatProcessMetadata& metadata; Core::System& system; + mutable Kernel::KProcess* cached_process{}; + mutable u64 cached_process_id{}; }; // Intermediary class that parses a text file or other disk format for storing cheats into a diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 2a5212fed8..d7edb3ffcb 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -239,7 +239,7 @@ void Reporter::SaveUnimplementedFunctionReport(Service::HLERequestContext& ctx, const auto title_id = system.GetApplicationProcessProgramID(); auto out = GetFullDataAuto(timestamp, title_id, system); - auto function_out = GetHLERequestContextData(ctx, system.ApplicationMemory()); + auto function_out = GetHLERequestContextData(ctx, ctx.GetMemory()); function_out["command_id"] = command_id; function_out["function_name"] = name; function_out["service_name"] = service_name; diff --git a/src/hid_core/hidbus/hidbus_base.cpp b/src/hid_core/hidbus/hidbus_base.cpp index 632bb173b6..e57121fa31 100644 --- a/src/hid_core/hidbus/hidbus_base.cpp +++ b/src/hid_core/hidbus/hidbus_base.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -62,8 +65,9 @@ void HidbusBase::DisablePollingMode() { polling_mode_enabled = false; } -void HidbusBase::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { +void HidbusBase::SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner) { transfer_memory = t_mem; + transfer_memory_owner = owner; } Kernel::KReadableEvent& HidbusBase::GetSendCommandAsycEvent() const { diff --git a/src/hid_core/hidbus/hidbus_base.h b/src/hid_core/hidbus/hidbus_base.h index c948606e40..5c29ada2e2 100644 --- a/src/hid_core/hidbus/hidbus_base.h +++ b/src/hid_core/hidbus/hidbus_base.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,6 +17,7 @@ class System; namespace Kernel { class KEvent; +class KProcess; class KReadableEvent; } // namespace Kernel @@ -138,7 +142,7 @@ public: void DisablePollingMode(); // Called on EnableJoyPollingReceiveMode - void SetTransferMemoryAddress(Common::ProcessAddress t_mem); + void SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner); Kernel::KReadableEvent& GetSendCommandAsycEvent() const; @@ -175,6 +179,7 @@ protected: ButtonOnlyPollingDataAccessor button_only_data{}; Common::ProcessAddress transfer_memory{}; + Kernel::KProcess* transfer_memory_owner{}; Core::System& system; Kernel::KEvent* send_command_async_event; diff --git a/src/hid_core/hidbus/ringcon.cpp b/src/hid_core/hidbus/ringcon.cpp index 74b4d7189b..a6798d22aa 100644 --- a/src/hid_core/hidbus/ringcon.cpp +++ b/src/hid_core/hidbus/ringcon.cpp @@ -6,6 +6,7 @@ #include "core/core.h" #include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_readable_event.h" #include "core/memory.h" #include "hid_core/frontend/emulated_controller.h" @@ -67,8 +68,10 @@ void RingController::OnUpdate() { curr_entry.polling_data.out_size = sizeof(ringcon_value); std::memcpy(curr_entry.polling_data.data.data(), &ringcon_value, sizeof(ringcon_value)); - system.ApplicationMemory().WriteBlock(transfer_memory, &enable_sixaxis_data, - sizeof(enable_sixaxis_data)); + if (transfer_memory_owner != nullptr) { + transfer_memory_owner->GetMemory().WriteBlock(transfer_memory, &enable_sixaxis_data, + sizeof(enable_sixaxis_data)); + } break; } default: diff --git a/src/hid_core/irsensor/image_transfer_processor.cpp b/src/hid_core/irsensor/image_transfer_processor.cpp index fb3e424d91..229433df79 100644 --- a/src/hid_core/irsensor/image_transfer_processor.cpp +++ b/src/hid_core/irsensor/image_transfer_processor.cpp @@ -1,10 +1,11 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include "core/core.h" +#include "core/hle/kernel/k_process.h" #include "core/memory.h" #include "hid_core/frontend/emulated_controller.h" #include "hid_core/hid_core.h" @@ -48,10 +49,12 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType if (type != Core::HID::ControllerTriggerType::IrSensor) { return; } - if (transfer_memory == 0) { + if (transfer_memory == 0 || transfer_memory_owner == nullptr) { return; } + auto& memory = transfer_memory_owner->GetMemory(); + const auto& camera_data = npad_device->GetCamera(); // This indicates how much ambient light is present @@ -61,16 +64,14 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType if (camera_data.format != current_config.origin_format) { LOG_WARNING(Service_IRS, "Wrong Input format {} expected {}", camera_data.format, current_config.origin_format); - system.ApplicationMemory().ZeroBlock(transfer_memory, - GetDataSize(current_config.trimming_format)); + memory.ZeroBlock(transfer_memory, GetDataSize(current_config.trimming_format)); return; } if (current_config.origin_format > current_config.trimming_format) { LOG_WARNING(Service_IRS, "Origin format {} is smaller than trimming format {}", current_config.origin_format, current_config.trimming_format); - system.ApplicationMemory().ZeroBlock(transfer_memory, - GetDataSize(current_config.trimming_format)); + memory.ZeroBlock(transfer_memory, GetDataSize(current_config.trimming_format)); return; } @@ -87,8 +88,7 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType "Trimming area ({}, {}, {}, {}) is outside of origin area ({}, {})", current_config.trimming_start_x, current_config.trimming_start_y, trimming_width, trimming_height, origin_width, origin_height); - system.ApplicationMemory().ZeroBlock(transfer_memory, - GetDataSize(current_config.trimming_format)); + memory.ZeroBlock(transfer_memory, GetDataSize(current_config.trimming_format)); return; } @@ -102,8 +102,8 @@ void ImageTransferProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType } } - system.ApplicationMemory().WriteBlock(transfer_memory, window_data.data(), - GetDataSize(current_config.trimming_format)); + memory.WriteBlock(transfer_memory, window_data.data(), + GetDataSize(current_config.trimming_format)); if (!IsProcessorActive()) { StartProcessor(); @@ -143,14 +143,19 @@ void ImageTransferProcessor::SetConfig( npad_device->SetCameraFormat(current_config.origin_format); } -void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { +void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_mem, + Kernel::KProcess* owner) { transfer_memory = t_mem; + transfer_memory_owner = owner; } Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( std::span data) const { + if (transfer_memory_owner == nullptr) + return processor_state; + const auto size = (std::min)(GetDataSize(current_config.trimming_format), data.size()); - system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size); + transfer_memory_owner->GetMemory().ReadBlock(transfer_memory, data.data(), size); return processor_state; } diff --git a/src/hid_core/irsensor/image_transfer_processor.h b/src/hid_core/irsensor/image_transfer_processor.h index df1c9d9208..f2f3dcba83 100644 --- a/src/hid_core/irsensor/image_transfer_processor.h +++ b/src/hid_core/irsensor/image_transfer_processor.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -9,6 +12,10 @@ #include "hid_core/irsensor/irs_types.h" #include "hid_core/irsensor/processor_base.h" +namespace Kernel { +class KProcess; +} + namespace Core { class System; } @@ -39,7 +46,7 @@ public: void SetConfig(Core::IrSensor::PackedImageTransferProcessorExConfig config); // Transfer memory where the image data will be stored - void SetTransferMemoryAddress(Common::ProcessAddress t_mem); + void SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner); Core::IrSensor::ImageTransferProcessorState GetState(std::span data) const; @@ -75,5 +82,6 @@ private: Core::System& system; Common::ProcessAddress transfer_memory{}; + Kernel::KProcess* transfer_memory_owner{}; }; } // namespace Service::IRS diff --git a/src/hid_core/resources/six_axis/seven_six_axis.cpp b/src/hid_core/resources/six_axis/seven_six_axis.cpp index d84ef31e10..584e6d6505 100644 --- a/src/hid_core/resources/six_axis/seven_six_axis.cpp +++ b/src/hid_core/resources/six_axis/seven_six_axis.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -6,6 +9,7 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" +#include "core/hle/kernel/k_process.h" #include "core/memory.h" #include "hid_core/frontend/emulated_console.h" #include "hid_core/frontend/emulated_devices.h" @@ -24,7 +28,7 @@ void SevenSixAxis::OnInit() {} void SevenSixAxis::OnRelease() {} void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!IsControllerActivated() || transfer_memory == 0) { + if (!IsControllerActivated() || transfer_memory == 0 || transfer_memory_owner == nullptr) { seven_sixaxis_lifo.buffer_count = 0; seven_sixaxis_lifo.buffer_tail = 0; return; @@ -51,12 +55,13 @@ void SevenSixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { }; seven_sixaxis_lifo.WriteNextEntry(next_seven_sixaxis_state); - system.ApplicationMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, - sizeof(seven_sixaxis_lifo)); + transfer_memory_owner->GetMemory().WriteBlock(transfer_memory, &seven_sixaxis_lifo, + sizeof(seven_sixaxis_lifo)); } -void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem) { +void SevenSixAxis::SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner) { transfer_memory = t_mem; + transfer_memory_owner = owner; } void SevenSixAxis::ResetTimestamp() { diff --git a/src/hid_core/resources/six_axis/seven_six_axis.h b/src/hid_core/resources/six_axis/seven_six_axis.h index 0a26c77c91..00afb8c00b 100644 --- a/src/hid_core/resources/six_axis/seven_six_axis.h +++ b/src/hid_core/resources/six_axis/seven_six_axis.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -9,6 +12,10 @@ #include "hid_core/resources/controller_base.h" #include "hid_core/resources/ring_lifo.h" +namespace Kernel { +class KProcess; +} + namespace Core { class System; } // namespace Core @@ -33,7 +40,7 @@ public: void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; // Called on InitializeSevenSixAxisSensor - void SetTransferMemoryAddress(Common::ProcessAddress t_mem); + void SetTransferMemoryAddress(Common::ProcessAddress t_mem, Kernel::KProcess* owner); // Called on ResetSevenSixAxisSensorTimestamp void ResetTimestamp(); @@ -58,6 +65,7 @@ private: SevenSixAxisState next_seven_sixaxis_state{}; Common::ProcessAddress transfer_memory{}; + Kernel::KProcess* transfer_memory_owner{}; Core::HID::EmulatedConsole* console = nullptr; Core::System& system; diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp index b095d6b458..51932678da 100644 --- a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp +++ b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp @@ -548,8 +548,18 @@ void TouchResource::OnTouchUpdate(s64 timestamp) { } auto& touch_shared = applet_data->shared_memory_format->touch_screen; - StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, bool(applet_data->flag.enable_touchscreen)); - touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state); + + if (applet_data->flag.enable_touchscreen) { + StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, true); + touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state); + } else { + TouchScreenState denied{}; + denied.sampling_number = current_touch_state.sampling_number; + denied.entry_count = 0; + data.finger_map.finger_count = 0; + data.finger_map.finger_ids = {}; + touch_shared.touch_screen_lifo.WriteNextEntry(denied); + } } } } diff --git a/src/qt_common/render/emu_thread.cpp b/src/qt_common/render/emu_thread.cpp index 1b65867509..d5e5522837 100644 --- a/src/qt_common/render/emu_thread.cpp +++ b/src/qt_common/render/emu_thread.cpp @@ -51,7 +51,18 @@ void EmuThread::run() { QtCommon::system->Run(); m_stopped.Reset(); - m_should_run_cv.wait(lk, stop_token, [&] { return !m_should_run; }); + m_should_run_cv.wait(lk, stop_token, [&] { + return !m_should_run || m_pending_shader_cache_title.has_value(); + }); + + if (m_should_run && m_pending_shader_cache_title.has_value()) { + const u64 program_id = *m_pending_shader_cache_title; + m_pending_shader_cache_title.reset(); + + lk.unlock(); + this->ReloadDiskShaderCache(program_id); + lk.lock(); + } } else { QtCommon::system->Pause(); m_stopped.Set(); @@ -67,6 +78,32 @@ void EmuThread::run() { QtCommon::system->ShutdownMainProcess(); } +void EmuThread::ReloadDiskShaderCache(u64 program_id) { + if (!Settings::values.use_disk_shader_cache.GetValue()) { + return; + } + + LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", program_id); + + auto& system = *QtCommon::system; + auto& gpu = system.GPU(); + + system.Pause(); + gpu.WaitForIdle(); + gpu.ObtainContext(); + + emit ShaderCacheReloadStarted(); + emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); + system.Renderer().ReadRasterizer()->LoadDiskResources(program_id, m_stop_source.get_token(), + [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { + emit LoadProgress(stage, value, total); + }); + emit LoadProgress(VideoCore::LoadCallbackStage::Complete, 0, 0); + emit ShaderCacheReloadFinished(); + + gpu.ReleaseContext(); +} + // Unlock while emitting signals so that the main thread can // continue pumping events. diff --git a/src/qt_common/render/emu_thread.h b/src/qt_common/render/emu_thread.h index 92bae50979..f7d0376ac9 100644 --- a/src/qt_common/render/emu_thread.h +++ b/src/qt_common/render/emu_thread.h @@ -3,7 +3,10 @@ #pragma once +#include + #include +#include "common/common_types.h" #include "common/logging.h" #include "common/thread.h" @@ -63,9 +66,19 @@ public: m_stop_source.request_stop(); } + /** + * Requests that the disk shader cache be reloaded for a different title. + */ + void RequestDiskShaderCacheReload(u64 program_id) { + std::unique_lock run_lk{m_should_run_mutex}; + m_pending_shader_cache_title = program_id; + m_should_run_cv.notify_one(); + } + private: void EmulationPaused(std::unique_lock& lk); void EmulationResumed(std::unique_lock& lk); + void ReloadDiskShaderCache(u64 program_id); private: std::stop_source m_stop_source; @@ -73,6 +86,7 @@ private: std::condition_variable_any m_should_run_cv; Common::Event m_stopped; bool m_should_run{true}; + std::optional m_pending_shader_cache_title; signals: /** @@ -94,4 +108,7 @@ signals: void DebugModeLeft(); void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total); + + void ShaderCacheReloadStarted(); + void ShaderCacheReloadFinished(); }; diff --git a/src/video_core/framebuffer_config.cpp b/src/video_core/framebuffer_config.cpp index e28d41f84c..7145b02c07 100644 --- a/src/video_core/framebuffer_config.cpp +++ b/src/video_core/framebuffer_config.cpp @@ -1,11 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/assert.h" #include "video_core/framebuffer_config.h" namespace Tegra { +std::span FilterLayerStack( + std::span layers, Service::Nvnflinger::LayerStackId stack, + std::vector& scratch) { + const u32 bit = Service::Nvnflinger::LayerStackBit(stack); + + if (std::ranges::all_of(layers, + [bit](const auto& layer) { return (layer.layer_stack_mask & bit) != 0; })) + return layers; + + scratch.clear(); + for (const auto& layer : layers) { + if ((layer.layer_stack_mask & bit) != 0) { + scratch.push_back(layer); + } + } + + return scratch; +} + Common::Rectangle NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, u32 texture_height) { f32 left, top, right, bottom; diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h index 8b2a49de5e..1fc70583f9 100644 --- a/src/video_core/framebuffer_config.h +++ b/src/video_core/framebuffer_config.h @@ -1,11 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once +#include +#include + #include "common/common_types.h" #include "common/math_util.h" #include "core/hle/service/nvnflinger/buffer_transform_flags.h" +#include "core/hle/service/nvnflinger/hwc_layer.h" #include "core/hle/service/nvnflinger/pixel_format.h" #include "core/hle/service/nvnflinger/ui/fence.h" @@ -30,9 +37,17 @@ struct FramebufferConfig { Service::android::BufferTransformFlags transform_flags{}; Common::Rectangle crop_rect{}; BlendMode blending{}; + u32 layer_stack_mask{Service::Nvnflinger::DefaultLayerStackMask}; }; Common::Rectangle NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, u32 texture_height); +/** + * Returns the subset of layers belonging to a stack. + */ +std::span FilterLayerStack(std::span layers, + Service::Nvnflinger::LayerStackId stack, + std::vector& scratch); + } // namespace Tegra diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index a44015c0dd..dbc7d43553 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -144,6 +144,12 @@ struct GPU::Impl { sync_request_cv.wait(lck, [this, fence] { return CurrentSyncRequestFence() >= fence; }); } + void WaitForIdle() { + const u64 fence = RequestSyncOperation([] {}); + gpu_thread.TickGPU(is_async); + WaitForSyncOperation(fence); + } + /// Tick pending requests within the GPU. void TickWork() { std::unique_lock lck{sync_request_mutex}; @@ -484,6 +490,10 @@ void GPU::NotifyShutdown() { impl->NotifyShutdown(); } +void GPU::WaitForIdle() { + impl->WaitForIdle(); +} + void GPU::ObtainContext() { impl->ObtainContext(); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 8d4a101fd4..75956ea199 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -168,6 +168,8 @@ public: void WaitForSyncOperation(u64 fence); + void WaitForIdle(); + /// Tick pending requests within the GPU. void TickWork(); diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 9b6fb69af3..e19a70ea01 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -36,7 +36,8 @@ bool RendererBase::IsScreenshotPending() const { } void RendererBase::RequestScreenshot(void* data, std::function callback, - const Layout::FramebufferLayout& layout) { + const Layout::FramebufferLayout& layout, + Service::Nvnflinger::LayerStackId layer_stack) { if (renderer_settings.screenshot_requested) { LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); return; @@ -48,6 +49,7 @@ void RendererBase::RequestScreenshot(void* data, std::function callb renderer_settings.screenshot_bits = data; renderer_settings.screenshot_complete_callback = async_callback; renderer_settings.screenshot_framebuffer_layout = layout; + renderer_settings.screenshot_layer_stack = layer_stack; renderer_settings.screenshot_requested = true; } diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 67427f9372..46f8276074 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2014 Citra Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -26,6 +29,7 @@ struct RendererSettings { void* screenshot_bits{}; std::function screenshot_complete_callback; Layout::FramebufferLayout screenshot_framebuffer_layout; + Service::Nvnflinger::LayerStackId screenshot_layer_stack{Service::Nvnflinger::LayerStackId::Default}; }; class RendererBase { @@ -88,9 +92,11 @@ public: /// Returns true if a screenshot is being processed bool IsScreenshotPending() const; - /// Request a screenshot of the next frame + /// Request a screenshot of the next frame. void RequestScreenshot(void* data, std::function callback, - const Layout::FramebufferLayout& layout); + const Layout::FramebufferLayout& layout, + Service::Nvnflinger::LayerStackId layer_stack = + Service::Nvnflinger::LayerStackId::Default); protected: Core::Frontend::EmuWindow& render_window; ///< Reference to the render window handle. diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 1c72147113..f9ec32e652 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -199,7 +199,10 @@ void RendererOpenGL::RenderScreenshot(std::span return; } - RenderToBuffer(framebuffers, renderer_settings.screenshot_framebuffer_layout, + const auto screenshot_layers = Tegra::FilterLayerStack( + framebuffers, renderer_settings.screenshot_layer_stack, screenshot_layer_scratch); + + RenderToBuffer(screenshot_layers, renderer_settings.screenshot_framebuffer_layout, renderer_settings.screenshot_bits); renderer_settings.screenshot_complete_callback(true); @@ -208,6 +211,12 @@ void RendererOpenGL::RenderScreenshot(std::span void RendererOpenGL::RenderAppletCaptureLayer( std::span framebuffers) { + const auto capture_layers = Tegra::FilterLayerStack( + framebuffers, Service::Nvnflinger::LayerStackId::LastFrame, applet_capture_layers); + + if (capture_layers.empty()) + return; + GLint old_read_fb; GLint old_draw_fb; glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); @@ -217,7 +226,7 @@ void RendererOpenGL::RenderAppletCaptureLayer( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, capture_renderbuffer.handle); - blit_applet->DrawScreen(framebuffers, VideoCore::Capture::Layout, true); + blit_applet->DrawScreen(capture_layers, VideoCore::Capture::Layout, true); glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index f694dc413a..5a6757e805 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2014 Citra Emulator Project @@ -61,6 +61,9 @@ private: void RenderScreenshot(std::span framebuffers); void RenderAppletCaptureLayer(std::span framebuffers); + std::vector applet_capture_layers; + std::vector screenshot_layer_scratch; + Core::Frontend::EmuWindow& emu_window; Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::GPU& gpu; diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index d815278c85..6290e79d93 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -292,8 +292,11 @@ void RendererVulkan::RenderScreenshot(std::span return; } + const auto screenshot_layers = Tegra::FilterLayerStack( + framebuffers, renderer_settings.screenshot_layer_stack, screenshot_layer_scratch); + const auto& layout{renderer_settings.screenshot_framebuffer_layout}; - const auto dst_buffer = RenderToBuffer(framebuffers, layout, VK_FORMAT_B8G8R8A8_UNORM, + const auto dst_buffer = RenderToBuffer(screenshot_layers, layout, VK_FORMAT_B8G8R8A8_UNORM, layout.width * layout.height * 4); std::memcpy(renderer_settings.screenshot_bits, dst_buffer.Mapped().data(), @@ -332,6 +335,12 @@ std::vector RendererVulkan::GetAppletCaptureBuffer() { void RendererVulkan::RenderAppletCaptureLayer( std::span framebuffers) { + const auto capture_layers = Tegra::FilterLayerStack( + framebuffers, Service::Nvnflinger::LayerStackId::LastFrame, applet_capture_layers); + + if (capture_layers.empty()) + return; + if (!applet_frame.image) { applet_frame.image = CreateWrappedImage(memory_allocator, CaptureImageSize, CaptureFormat); applet_frame.image_view = CreateWrappedImageView(device, applet_frame.image, CaptureFormat); @@ -340,7 +349,7 @@ void RendererVulkan::RenderAppletCaptureLayer( } scheduler.RequestOutsideRenderPassOperationContext(); - blit_applet.DrawToFrame(device, rasterizer, &applet_frame, framebuffers, VideoCore::Capture::Layout, 1, + blit_applet.DrawToFrame(device, rasterizer, &applet_frame, capture_layers, VideoCore::Capture::Layout, 1, CaptureFormat); } diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 5125fadcd7..87422dd935 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -75,6 +75,9 @@ private: void RenderScreenshot(std::span framebuffers); void RenderAppletCaptureLayer(std::span framebuffers); + std::vector applet_capture_layers; + std::vector screenshot_layer_scratch; + Tegra::MaxwellDeviceMemoryManager& device_memory; Tegra::GPU& gpu; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 130b5e4cc2..6e6120d1b1 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -592,6 +592,14 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading if (title_id == 0) { return; } + if (!pipeline_cache_filename.empty()) { + serialization_thread.WaitForRequests(); + if (use_vulkan_pipeline_cache && !vulkan_pipeline_cache_filename.empty()) { + SerializeVulkanPipelineCache(vulkan_pipeline_cache_filename, vulkan_pipeline_cache, + CACHE_VERSION); + } + } + const auto shader_dir{Common::FS::GetEdenPath(Common::FS::EdenPath::ShaderDir)}; const auto base_dir{shader_dir / fmt::format("{:016x}", title_id)}; if (!Common::FS::CreateDir(shader_dir) || !Common::FS::CreateDir(base_dir)) { diff --git a/src/yuzu/main_window.cpp b/src/yuzu/main_window.cpp index f18a989255..934879ade5 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -139,6 +139,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "core/hle/service/am/frontend/applet_web_browser_types.h" #include "core/file_sys/card_image.h" +#include "core/file_sys/common_funcs.h" #include "core/file_sys/romfs.h" #include "core/file_sys/savedata_factory.h" @@ -1967,12 +1968,37 @@ void MainWindow::BootGame(const QString& filename, Service::AM::FrontendAppletPa render_window->Exit(); }); + QtCommon::system->RegisterApplicationChangedCallback([this](u64 changed_program_id) { + if (QtCommon::emu_thread) + QtCommon::emu_thread->RequestDiskShaderCacheReload(changed_program_id); + + QMetaObject::invokeMethod( + this, [this, changed_program_id] { this->OnApplicationChanged(changed_program_id); }, + Qt::QueuedConnection); + }); + connect(render_window, &GRenderWindow::Closed, this, &MainWindow::OnStopGame); connect(render_window, &GRenderWindow::MouseActivity, this, &MainWindow::OnMouseActivity); connect(QtCommon::emu_thread.get(), &EmuThread::LoadProgress, loading_screen, &LoadingScreen::OnLoadProgress, Qt::QueuedConnection); + connect( + QtCommon::emu_thread.get(), &EmuThread::ShaderCacheReloadStarted, this, + [this] { + loading_screen->Prepare(QtCommon::system->GetAppLoader()); + loading_screen->show(); + render_window->hide(); + }, + Qt::QueuedConnection); + + connect( + QtCommon::emu_thread.get(), &EmuThread::ShaderCacheReloadFinished, this, + [this] { + loading_screen->OnLoadComplete(); + }, + Qt::QueuedConnection); + // Update the GUI UpdateStatusButtons(); if (ui->action_Single_Window_Mode->isChecked()) { @@ -3061,12 +3087,31 @@ void MainWindow::OnLoadComplete() { } void MainWindow::OnExecuteProgram(std::size_t program_index) { + const u64 previous_program_id = QtCommon::system->GetApplicationProcessProgramID(); + + const auto current_path = + QString::fromStdString(QtCommon::system->GetCurrentApplicationFilePath()); + + LOG_INFO(Frontend, "ExecuteProgram requested, program_index={} previous_program_id={:016X}", + program_index, previous_program_id); + ShutdownGame(); auto params = ApplicationAppletParameters(); params.program_index = static_cast(program_index); params.launch_type = Service::AM::LaunchType::ApplicationInitiated; - BootGame(last_filename_booted, params); + + if (previous_program_id > static_cast(Service::AM::AppletProgramId::MaxProgramId)) { + params.previous_program_index = + static_cast(previous_program_id - FileSys::GetBaseTitleID(previous_program_id)); + params.program_id = previous_program_id; + } + + const auto filename = current_path.isEmpty() ? last_filename_booted : current_path; + + LOG_DEBUG(Frontend, "ExecuteProgram booting from path: {}", filename.toStdString()); + + BootGame(filename, params); } void MainWindow::OnExit() { @@ -3995,6 +4040,41 @@ void MainWindow::OnEmulatorUpdateAvailable() { } #endif +void MainWindow::OnApplicationChanged(u64 program_id) { + if (!emulation_running || program_id == 0) + return; + + std::string title_name; + std::string title_version; + + const FileSys::PatchManager pm(program_id, QtCommon::system->GetFileSystemController(), + QtCommon::system->GetContentProvider()); + if (const auto metadata = pm.GetControlMetadata(); metadata.first != nullptr) { + title_version = metadata.first->GetVersionString(); + title_name = metadata.first->GetApplicationName(); + } + if (title_name.empty()) { + title_name = fmt::format("{:016X}", program_id); + } + + if (const auto* process = QtCommon::system->Kernel().ApplicationProcess(); process != nullptr) { + const auto instruction_set_suffix = process->Is64Bit() ? tr("(64-bit)") : tr("(32-bit)"); + title_name = + tr("%1 %2", "%1 is the title name. %2 indicates if the title is 64-bit or 32-bit") + .arg(QString::fromStdString(title_name), instruction_set_suffix) + .toStdString(); + } + + LOG_INFO(Frontend, "Now running: {:016X} | {} | {}", program_id, title_name, title_version); + + UpdateWindowTitle(title_name, title_version, + QtCommon::system->GPU().Renderer().GetDeviceVendor()); + + // Switch to record playtime for the running program + if (play_time_manager) + play_time_manager->SetProgramId(program_id); +} + void MainWindow::UpdateWindowTitle(std::string_view title_name, std::string_view title_version, std::string_view gpu_vendor) { static const std::string build_id = std::string{Common::g_build_id}; diff --git a/src/yuzu/main_window.h b/src/yuzu/main_window.h index 2b50e3af91..3fe7ec620f 100644 --- a/src/yuzu/main_window.h +++ b/src/yuzu/main_window.h @@ -438,6 +438,7 @@ private: ContentManager::InstallResult InstallNCA(const QString& filename); void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, std::string_view gpu_vendor = {}); + void OnApplicationChanged(u64 program_id); void UpdateDockedButton(); void UpdateAPIText(); void UpdateFilterText(); diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 27b5746ef3..a161418289 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include "common/settings_enums.h" @@ -34,6 +35,8 @@ #include "input_common/main.h" #include "network/network.h" #include "sdl_config.h" +#include "video_core/gpu.h" +#include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" #include "yuzu_cmd/emu_window/emu_window_sdl3.h" #ifdef HAS_OPENGL @@ -463,6 +466,30 @@ extern "C" SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv) { // don't do anything, SDL3 already exists for us :D state->system.RegisterExitCallback([] {}); + + // QLaunch launched applications (should) now reload shader cache. + static std::mutex shader_cache_reload_mutex; + state->system.RegisterApplicationChangedCallback([&state](u64 changed_program_id) { + if (!Settings::values.use_disk_shader_cache.GetValue()) { + return; + } + + std::scoped_lock lk{shader_cache_reload_mutex}; + + LOG_INFO(Frontend, "Reloading disk shader cache for {:016X}", changed_program_id); + + state->system.Pause(); + state->system.GPU().WaitForIdle(); + state->system.GPU().ObtainContext(); + + state->system.Renderer().ReadRasterizer()->LoadDiskResources( + changed_program_id, std::stop_token{}, + [](VideoCore::LoadCallbackStage, size_t, size_t) {}); + + state->system.GPU().ReleaseContext(); + state->system.Run(); + }); + void(state->system.Run()); if (state->system.DebuggerEnabled()) state->system.InitializeDebugger();