From 31e4ecea58e169955fb12903a4c314881b851d05 Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Wed, 8 Jul 2026 23:30:57 +0200 Subject: [PATCH] core: apt: Restore captured framebuffers on libapplet closure --- src/core/hle/service/apt/applet_manager.cpp | 72 ++++++++++++--------- src/core/hle/service/apt/applet_manager.h | 33 ++++++---- src/core/hle/service/apt/apt.cpp | 2 +- 3 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 1bc2c0266..faed80672 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -274,8 +274,9 @@ void AppletManager::CancelAndSendParameter(const MessageParameter& parameter) { parameter.sender_id); if (parameter.buffer.size() >= sizeof(CaptureBufferInfo)) { - SetCaptureInfo(parameter.buffer); + SetCaptureInfoSuspendedApp(parameter.buffer); CaptureFrameBuffers(); + TransferCapturedFramebuffers(); } next_parameter->sender_id = parameter.destination_id; @@ -616,8 +617,6 @@ Result AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) { last_library_launcher_slot = active_slot; last_prepared_library_applet = applet_id; - capture_buffer_info.reset(); - if (Settings::values.lle_applets) { bool is_setup = system.GetAppLoader().DoingInitialSetup(); auto cfg = Service::CFG::GetModule(system); @@ -638,6 +637,8 @@ Result AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) { LOG_DEBUG(Service_APT, "Creating HLE applet {:03X} with parent {:03X}", applet_id, parent); return CreateHLEApplet(applet_id, parent, false); } + + capture_info.reset(); } Result AppletManager::PreloadLibraryApplet(AppletId applet_id) { @@ -737,6 +738,14 @@ Result AppletManager::CloseLibraryApplet(std::shared_ptr object, SendParameter(param); } + // TODO: This doesn't work correctly when opening the + // theme shop and an error showwing, because for + // some reason the theme shop is an application. + if (last_library_launcher_slot == AppletSlot::SystemApplet && + GetAppletSlotFromId(AppletId::Application) != AppletSlot::Error) { + TransferCapturedFramebuffers(); + } + return ResultSuccess; } @@ -971,7 +980,7 @@ Result AppletManager::PrepareToJumpToHomeMenu() { last_jump_to_home_slot = active_slot; - capture_buffer_info.reset(); + capture_info.reset(); if (last_jump_to_home_slot == AppletSlot::Application) { EnsureHomeMenuLoaded(); @@ -1465,7 +1474,7 @@ Result AppletManager::PrepareToStartApplication(u64 title_id, FS::MediaType medi app_start_parameters->next_title_id = title_id; app_start_parameters->next_media_type = media_type; - capture_buffer_info.reset(); + capture_info.reset(); app_jump_parameters.Invalidate(); @@ -1632,8 +1641,8 @@ static u32 GetDisplayBufferModePixelSize(DisplayBufferMode mode) { } } -static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr src, u32 height, - DisplayBufferMode mode) { +static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, std::span dst, + VAddr src, u32 height, DisplayBufferMode mode) { const auto bpp = GetDisplayBufferModePixelSize(mode); if (bpp == 0) { return; @@ -1642,15 +1651,8 @@ static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr s system.Memory().RasterizerFlushVirtualRegion(src, GSP::FRAMEBUFFER_WIDTH * height * bpp, Memory::FlushMode::Flush); - // Address in VRAM that APT copies framebuffer captures to. - constexpr VAddr screen_capture_base_vaddr = Memory::VRAM_VADDR + 0x500000; - const auto dst_vaddr = screen_capture_base_vaddr + capture_offset; - auto dst_ptr = system.Memory().GetPointer(dst_vaddr); - if (!dst_ptr) { - LOG_ERROR(Service_APT, - "Could not retrieve framebuffer capture destination buffer, skipping screen."); - return; - } + // APT captures the framebuffer to a copy on its own bss. + u8* dst_ptr = dst.data() + capture_offset; const auto src_ptr = system.Memory().GetPointer(src); if (!src_ptr) { @@ -1664,28 +1666,40 @@ static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, VAddr s const auto dst_offset = VideoCore::GetMortonOffset(x, y, bpp) + (y & ~7) * GSP::FRAMEBUFFER_WIDTH_POW2 * bpp; const auto src_offset = bpp * (GSP::FRAMEBUFFER_WIDTH * y + x); + ASSERT(capture_offset + dst_offset + bpp <= dst.size()); std::memcpy(dst_ptr + dst_offset, src_ptr + src_offset, bpp); } } - - system.Memory().RasterizerFlushVirtualRegion( - dst_vaddr, GSP::FRAMEBUFFER_WIDTH_POW2 * height * bpp, Memory::FlushMode::Invalidate); } void AppletManager::CaptureFrameBuffers() { - CaptureFrameBuffer(system, capture_info->bottom_screen_left_offset, - GSP::FRAMEBUFFER_SAVE_AREA_BOTTOM, GSP::BOTTOM_FRAMEBUFFER_HEIGHT, - capture_info->bottom_screen_format); - CaptureFrameBuffer(system, capture_info->top_screen_left_offset, - GSP::FRAMEBUFFER_SAVE_AREA_TOP_LEFT, GSP::TOP_FRAMEBUFFER_HEIGHT, - capture_info->top_screen_format); - if (capture_info->is_3d) { - CaptureFrameBuffer(system, capture_info->top_screen_right_offset, - GSP::FRAMEBUFFER_SAVE_AREA_TOP_RIGHT, GSP::TOP_FRAMEBUFFER_HEIGHT, - capture_info->top_screen_format); + CaptureFrameBuffer(system, capture_info_suspended_app->bottom_screen_left_offset, + framebuffer_backup, GSP::FRAMEBUFFER_SAVE_AREA_BOTTOM, + GSP::BOTTOM_FRAMEBUFFER_HEIGHT, + capture_info_suspended_app->bottom_screen_format); + CaptureFrameBuffer(system, capture_info_suspended_app->top_screen_left_offset, + framebuffer_backup, GSP::FRAMEBUFFER_SAVE_AREA_TOP_LEFT, + GSP::TOP_FRAMEBUFFER_HEIGHT, capture_info_suspended_app->top_screen_format); + if (capture_info_suspended_app->is_3d) { + CaptureFrameBuffer(system, capture_info_suspended_app->top_screen_right_offset, + framebuffer_backup, GSP::FRAMEBUFFER_SAVE_AREA_TOP_RIGHT, + GSP::TOP_FRAMEBUFFER_HEIGHT, + capture_info_suspended_app->top_screen_format); } } +void AppletManager::TransferCapturedFramebuffers() { + constexpr u32 VRAM_TRANSFER_OFFSET = 0x500000; + + const VAddr dst_vaddr = Memory::VRAM_VADDR + VRAM_TRANSFER_OFFSET; + auto dst_ptr = system.Memory().GetPointer(dst_vaddr); + + memcpy(dst_ptr, framebuffer_backup.data(), framebuffer_backup.size()); + + system.Memory().RasterizerFlushVirtualRegion(dst_vaddr, framebuffer_backup.size(), + Memory::FlushMode::Invalidate); +} + void AppletManager::LoadInputDevices() { home_button = Input::CreateDevice( Settings::values.current_input_profile.buttons[Settings::NativeButton::Home]); diff --git a/src/core/hle/service/apt/applet_manager.h b/src/core/hle/service/apt/applet_manager.h index 507841dd5..c6306d2d5 100644 --- a/src/core/hle/service/apt/applet_manager.h +++ b/src/core/hle/service/apt/applet_manager.h @@ -367,35 +367,36 @@ public: deliver_arg = std::move(arg); } - std::vector GetCaptureInfo() { + std::vector GetCaptureInfoSuspendedApp() { std::vector buffer; - if (capture_info) { + if (capture_info_suspended_app) { buffer.resize(sizeof(CaptureBufferInfo)); - std::memcpy(buffer.data(), &capture_info.get(), sizeof(CaptureBufferInfo)); + std::memcpy(buffer.data(), &capture_info_suspended_app.get(), + sizeof(CaptureBufferInfo)); } return buffer; } - void SetCaptureInfo(std::vector buffer) { + void SetCaptureInfoSuspendedApp(std::vector buffer) { ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small."); - capture_info.emplace(); - std::memcpy(&capture_info.get(), buffer.data(), sizeof(CaptureBufferInfo)); + capture_info_suspended_app.emplace(); + std::memcpy(&capture_info_suspended_app.get(), buffer.data(), sizeof(CaptureBufferInfo)); } std::vector ReceiveCaptureBufferInfo() { std::vector buffer; - if (capture_buffer_info) { + if (capture_info) { buffer.resize(sizeof(CaptureBufferInfo)); - std::memcpy(buffer.data(), &capture_buffer_info.get(), sizeof(CaptureBufferInfo)); - capture_buffer_info.reset(); + std::memcpy(buffer.data(), &capture_info.get(), sizeof(CaptureBufferInfo)); + capture_info.reset(); } return buffer; } void SendCaptureBufferInfo(std::vector buffer) { ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small."); - capture_buffer_info.emplace(); - std::memcpy(&capture_buffer_info.get(), buffer.data(), sizeof(CaptureBufferInfo)); + capture_info.emplace(); + std::memcpy(&capture_info.get(), buffer.data(), sizeof(CaptureBufferInfo)); } Result PrepareToStartApplication(u64 title_id, FS::MediaType media_type); @@ -451,8 +452,11 @@ private: boost::optional sys_menu_arg{}; u64 home_menu_tid_to_start{}; + boost::optional capture_info_suspended_app; boost::optional capture_info; - boost::optional capture_buffer_info; + + static constexpr size_t FRAMEBUFFER_BACKUP_SIZE = 0xE7000; + std::vector framebuffer_backup = std::vector(FRAMEBUFFER_BACKUP_SIZE); static constexpr std::size_t NumAppletSlot = 4; @@ -555,6 +559,7 @@ private: void EnsureHomeMenuLoaded(); void CaptureFrameBuffers(); + void TransferCapturedFramebuffers(); Result CreateHLEApplet(AppletId id, AppletId parent, bool preload); void HLEAppletUpdateEvent(std::uintptr_t user_data, s64 cycles_late); @@ -571,8 +576,9 @@ private: ar & deliver_arg; ar & sys_menu_arg; ar & home_menu_tid_to_start; + ar & capture_info_suspended_app; ar & capture_info; - ar & capture_buffer_info; + ar & framebuffer_backup; ar & active_slot; ar & last_library_launcher_slot; ar & last_prepared_library_applet; @@ -584,7 +590,6 @@ private: ar & application_close_target; ar & new_3ds_mode_blocked; ar & lock; - ar & capture_info; ar & applet_slots; ar & library_applet_closing_command; ar & next_app_mediatype; diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 2fbaeca65..30d2f08a3 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -1123,7 +1123,7 @@ void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_APT, "called"); - auto screen_capture_buffer = apt->applet_manager->GetCaptureInfo(); + auto screen_capture_buffer = apt->applet_manager->GetCaptureInfoSuspendedApp(); auto real_size = std::min(static_cast(screen_capture_buffer.size()), size); screen_capture_buffer.resize(size);