core: apt: Restore captured framebuffers on libapplet closure

This commit is contained in:
PabloMK7 2026-07-08 23:30:57 +02:00
parent 49ba2e8d5f
commit 31e4ecea58
3 changed files with 63 additions and 44 deletions

View File

@ -274,8 +274,9 @@ void AppletManager::CancelAndSendParameter(const MessageParameter& parameter) {
parameter.sender_id); parameter.sender_id);
if (parameter.buffer.size() >= sizeof(CaptureBufferInfo)) { if (parameter.buffer.size() >= sizeof(CaptureBufferInfo)) {
SetCaptureInfo(parameter.buffer); SetCaptureInfoSuspendedApp(parameter.buffer);
CaptureFrameBuffers(); CaptureFrameBuffers();
TransferCapturedFramebuffers();
} }
next_parameter->sender_id = parameter.destination_id; next_parameter->sender_id = parameter.destination_id;
@ -616,8 +617,6 @@ Result AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) {
last_library_launcher_slot = active_slot; last_library_launcher_slot = active_slot;
last_prepared_library_applet = applet_id; last_prepared_library_applet = applet_id;
capture_buffer_info.reset();
if (Settings::values.lle_applets) { if (Settings::values.lle_applets) {
bool is_setup = system.GetAppLoader().DoingInitialSetup(); bool is_setup = system.GetAppLoader().DoingInitialSetup();
auto cfg = Service::CFG::GetModule(system); 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); LOG_DEBUG(Service_APT, "Creating HLE applet {:03X} with parent {:03X}", applet_id, parent);
return CreateHLEApplet(applet_id, parent, false); return CreateHLEApplet(applet_id, parent, false);
} }
capture_info.reset();
} }
Result AppletManager::PreloadLibraryApplet(AppletId applet_id) { Result AppletManager::PreloadLibraryApplet(AppletId applet_id) {
@ -737,6 +738,14 @@ Result AppletManager::CloseLibraryApplet(std::shared_ptr<Kernel::Object> object,
SendParameter(param); 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; return ResultSuccess;
} }
@ -971,7 +980,7 @@ Result AppletManager::PrepareToJumpToHomeMenu() {
last_jump_to_home_slot = active_slot; last_jump_to_home_slot = active_slot;
capture_buffer_info.reset(); capture_info.reset();
if (last_jump_to_home_slot == AppletSlot::Application) { if (last_jump_to_home_slot == AppletSlot::Application) {
EnsureHomeMenuLoaded(); 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_title_id = title_id;
app_start_parameters->next_media_type = media_type; app_start_parameters->next_media_type = media_type;
capture_buffer_info.reset(); capture_info.reset();
app_jump_parameters.Invalidate(); 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, static void CaptureFrameBuffer(Core::System& system, u32 capture_offset, std::span<u8> dst,
DisplayBufferMode mode) { VAddr src, u32 height, DisplayBufferMode mode) {
const auto bpp = GetDisplayBufferModePixelSize(mode); const auto bpp = GetDisplayBufferModePixelSize(mode);
if (bpp == 0) { if (bpp == 0) {
return; 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, system.Memory().RasterizerFlushVirtualRegion(src, GSP::FRAMEBUFFER_WIDTH * height * bpp,
Memory::FlushMode::Flush); Memory::FlushMode::Flush);
// Address in VRAM that APT copies framebuffer captures to. // APT captures the framebuffer to a copy on its own bss.
constexpr VAddr screen_capture_base_vaddr = Memory::VRAM_VADDR + 0x500000; u8* dst_ptr = dst.data() + capture_offset;
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;
}
const auto src_ptr = system.Memory().GetPointer(src); const auto src_ptr = system.Memory().GetPointer(src);
if (!src_ptr) { 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) + const auto dst_offset = VideoCore::GetMortonOffset(x, y, bpp) +
(y & ~7) * GSP::FRAMEBUFFER_WIDTH_POW2 * bpp; (y & ~7) * GSP::FRAMEBUFFER_WIDTH_POW2 * bpp;
const auto src_offset = bpp * (GSP::FRAMEBUFFER_WIDTH * y + x); 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); 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() { void AppletManager::CaptureFrameBuffers() {
CaptureFrameBuffer(system, capture_info->bottom_screen_left_offset, CaptureFrameBuffer(system, capture_info_suspended_app->bottom_screen_left_offset,
GSP::FRAMEBUFFER_SAVE_AREA_BOTTOM, GSP::BOTTOM_FRAMEBUFFER_HEIGHT, framebuffer_backup, GSP::FRAMEBUFFER_SAVE_AREA_BOTTOM,
capture_info->bottom_screen_format); GSP::BOTTOM_FRAMEBUFFER_HEIGHT,
CaptureFrameBuffer(system, capture_info->top_screen_left_offset, capture_info_suspended_app->bottom_screen_format);
GSP::FRAMEBUFFER_SAVE_AREA_TOP_LEFT, GSP::TOP_FRAMEBUFFER_HEIGHT, CaptureFrameBuffer(system, capture_info_suspended_app->top_screen_left_offset,
capture_info->top_screen_format); framebuffer_backup, GSP::FRAMEBUFFER_SAVE_AREA_TOP_LEFT,
if (capture_info->is_3d) { GSP::TOP_FRAMEBUFFER_HEIGHT, capture_info_suspended_app->top_screen_format);
CaptureFrameBuffer(system, capture_info->top_screen_right_offset, if (capture_info_suspended_app->is_3d) {
GSP::FRAMEBUFFER_SAVE_AREA_TOP_RIGHT, GSP::TOP_FRAMEBUFFER_HEIGHT, CaptureFrameBuffer(system, capture_info_suspended_app->top_screen_right_offset,
capture_info->top_screen_format); 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() { void AppletManager::LoadInputDevices() {
home_button = Input::CreateDevice<Input::ButtonDevice>( home_button = Input::CreateDevice<Input::ButtonDevice>(
Settings::values.current_input_profile.buttons[Settings::NativeButton::Home]); Settings::values.current_input_profile.buttons[Settings::NativeButton::Home]);

View File

@ -367,35 +367,36 @@ public:
deliver_arg = std::move(arg); deliver_arg = std::move(arg);
} }
std::vector<u8> GetCaptureInfo() { std::vector<u8> GetCaptureInfoSuspendedApp() {
std::vector<u8> buffer; std::vector<u8> buffer;
if (capture_info) { if (capture_info_suspended_app) {
buffer.resize(sizeof(CaptureBufferInfo)); 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; return buffer;
} }
void SetCaptureInfo(std::vector<u8> buffer) { void SetCaptureInfoSuspendedApp(std::vector<u8> buffer) {
ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small."); ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small.");
capture_info.emplace(); capture_info_suspended_app.emplace();
std::memcpy(&capture_info.get(), buffer.data(), sizeof(CaptureBufferInfo)); std::memcpy(&capture_info_suspended_app.get(), buffer.data(), sizeof(CaptureBufferInfo));
} }
std::vector<u8> ReceiveCaptureBufferInfo() { std::vector<u8> ReceiveCaptureBufferInfo() {
std::vector<u8> buffer; std::vector<u8> buffer;
if (capture_buffer_info) { if (capture_info) {
buffer.resize(sizeof(CaptureBufferInfo)); buffer.resize(sizeof(CaptureBufferInfo));
std::memcpy(buffer.data(), &capture_buffer_info.get(), sizeof(CaptureBufferInfo)); std::memcpy(buffer.data(), &capture_info.get(), sizeof(CaptureBufferInfo));
capture_buffer_info.reset(); capture_info.reset();
} }
return buffer; return buffer;
} }
void SendCaptureBufferInfo(std::vector<u8> buffer) { void SendCaptureBufferInfo(std::vector<u8> buffer) {
ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small."); ASSERT_MSG(buffer.size() >= sizeof(CaptureBufferInfo), "CaptureBufferInfo is too small.");
capture_buffer_info.emplace(); capture_info.emplace();
std::memcpy(&capture_buffer_info.get(), buffer.data(), sizeof(CaptureBufferInfo)); std::memcpy(&capture_info.get(), buffer.data(), sizeof(CaptureBufferInfo));
} }
Result PrepareToStartApplication(u64 title_id, FS::MediaType media_type); Result PrepareToStartApplication(u64 title_id, FS::MediaType media_type);
@ -451,8 +452,11 @@ private:
boost::optional<SysMenuArg> sys_menu_arg{}; boost::optional<SysMenuArg> sys_menu_arg{};
u64 home_menu_tid_to_start{}; u64 home_menu_tid_to_start{};
boost::optional<CaptureBufferInfo> capture_info_suspended_app;
boost::optional<CaptureBufferInfo> capture_info; boost::optional<CaptureBufferInfo> capture_info;
boost::optional<CaptureBufferInfo> capture_buffer_info;
static constexpr size_t FRAMEBUFFER_BACKUP_SIZE = 0xE7000;
std::vector<u8> framebuffer_backup = std::vector<u8>(FRAMEBUFFER_BACKUP_SIZE);
static constexpr std::size_t NumAppletSlot = 4; static constexpr std::size_t NumAppletSlot = 4;
@ -555,6 +559,7 @@ private:
void EnsureHomeMenuLoaded(); void EnsureHomeMenuLoaded();
void CaptureFrameBuffers(); void CaptureFrameBuffers();
void TransferCapturedFramebuffers();
Result CreateHLEApplet(AppletId id, AppletId parent, bool preload); Result CreateHLEApplet(AppletId id, AppletId parent, bool preload);
void HLEAppletUpdateEvent(std::uintptr_t user_data, s64 cycles_late); void HLEAppletUpdateEvent(std::uintptr_t user_data, s64 cycles_late);
@ -571,8 +576,9 @@ private:
ar & deliver_arg; ar & deliver_arg;
ar & sys_menu_arg; ar & sys_menu_arg;
ar & home_menu_tid_to_start; ar & home_menu_tid_to_start;
ar & capture_info_suspended_app;
ar & capture_info; ar & capture_info;
ar & capture_buffer_info; ar & framebuffer_backup;
ar & active_slot; ar & active_slot;
ar & last_library_launcher_slot; ar & last_library_launcher_slot;
ar & last_prepared_library_applet; ar & last_prepared_library_applet;
@ -584,7 +590,6 @@ private:
ar & application_close_target; ar & application_close_target;
ar & new_3ds_mode_blocked; ar & new_3ds_mode_blocked;
ar & lock; ar & lock;
ar & capture_info;
ar & applet_slots; ar & applet_slots;
ar & library_applet_closing_command; ar & library_applet_closing_command;
ar & next_app_mediatype; ar & next_app_mediatype;

View File

@ -1123,7 +1123,7 @@ void Module::APTInterface::GetCaptureInfo(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_APT, "called"); 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<u32>(screen_capture_buffer.size()), size); auto real_size = std::min(static_cast<u32>(screen_capture_buffer.size()), size);
screen_capture_buffer.resize(size); screen_capture_buffer.resize(size);