From 25e6db0b363430ef6a24b2ca5b0a7dff94e3b9e9 Mon Sep 17 00:00:00 2001 From: Maufeat Date: Thu, 30 Jul 2026 20:34:50 +0200 Subject: [PATCH] a lot --- src/android/app/src/main/jni/native.cpp | 49 ++- src/android/app/src/main/jni/native.h | 5 + src/core/core.cpp | 35 +- src/core/core.h | 8 + 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 +- .../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 | 2 +- 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 | 3 +- 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 | 6 +- .../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/nso.cpp | 5 +- src/core/memory/cheat_engine.cpp | 37 +- src/core/memory/cheat_engine.h | 8 + src/core/reporter.cpp | 2 +- src/hid_core/hidbus/hidbus_base.cpp | 3 +- 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 | 4 +- 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 | 5 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 8 + src/yuzu/main_window.cpp | 60 ++++ src/yuzu/main_window.h | 1 + src/yuzu_cmd/yuzu.cpp | 27 ++ 76 files changed, 1141 insertions(+), 329 deletions(-) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 41ba1b2019..a7a63c8102 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -325,6 +325,9 @@ 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_index = program_index_; @@ -404,20 +407,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..288835a835 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; diff --git a/src/core/core.cpp b/src/core/core.cpp index 3ff42cf6f5..975b4f2901 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -329,7 +329,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)}; @@ -465,6 +465,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; @@ -725,7 +726,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 { @@ -959,6 +978,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..c6c0b810ac 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; @@ -435,6 +439,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/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/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 af0b9b16ad..63a033eaf0 100644 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -248,7 +248,7 @@ Result AlbumManager::SaveScreenShot(ApplicationAlbumEntry& out_entry, AlbumReportOption report_option, const ApplicationData& app_data, std::span image_data, u64 aruid) { - 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 6227fc44cc..44b94742bb 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 @@ -18,6 +18,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" @@ -336,8 +337,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 bdbb16ed04..9db47cdc22 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -76,6 +76,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 cfd999497b..478817b41b 100644 --- a/src/core/hle/service/nvnflinger/hardware_composer.cpp +++ b/src/core/hle/service/nvnflinger/hardware_composer.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 @@ -125,6 +125,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 1c96360561..2c1ab6e66a 100644 --- a/src/core/hle/service/pctl/parental_control_service.h +++ b/src/core/hle/service/pctl/parental_control_service.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 @@ -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 as fuck 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/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/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..9cd4f42511 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h @@ -15,6 +15,10 @@ namespace Core { class System; } +namespace Kernel { +class KProcess; +} + namespace Core::Timing { class CoreTiming; struct EventType; @@ -38,8 +42,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..6446f4701e 100644 --- a/src/hid_core/hidbus/hidbus_base.cpp +++ b/src/hid_core/hidbus/hidbus_base.cpp @@ -62,8 +62,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 548a8bf809..1544c74610 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -130,6 +130,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}; @@ -456,6 +462,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 538c4da85a..e16e7ae1cb 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.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 2018 yuzu Emulator Project @@ -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 b0a7d0c579..dcd8757378 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -252,8 +252,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(), @@ -292,6 +295,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); @@ -300,7 +309,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 4fb88b29de..a12e2c23fc 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.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 2018 yuzu Emulator Project @@ -72,6 +72,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 b1b424ca08..4a77b3ba6c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -586,6 +586,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 9b6b0a8b6a..cac1ee7a95 100644 --- a/src/yuzu/main_window.cpp +++ b/src/yuzu/main_window.cpp @@ -1966,12 +1966,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()) { @@ -3994,6 +4019,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 d0dcae1561..9ce32eadb4 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -28,6 +29,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 @@ -462,6 +465,30 @@ int main(int argc, char** argv) { // Just exit right away. exit(0); }); + + // QLaunch launched applications (should) now reload shader cache. + static std::mutex shader_cache_reload_mutex; + system.RegisterApplicationChangedCallback([&system](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); + + system.Pause(); + system.GPU().WaitForIdle(); + system.GPU().ObtainContext(); + + system.Renderer().ReadRasterizer()->LoadDiskResources( + changed_program_id, std::stop_token{}, + [](VideoCore::LoadCallbackStage, size_t, size_t) {}); + + system.GPU().ReleaseContext(); + system.Run(); + }); + void(system.Run()); if (system.DebuggerEnabled()) { system.InitializeDebugger();