mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-03-28 06:10:09 -06:00
core: Fix home menu garbled suspended application image (#1518)
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux (appimage) (push) Has been cancelled
citra-build / linux (appimage-wayland) (push) Has been cancelled
citra-build / linux (fresh) (push) Has been cancelled
citra-build / macos (arm64) (push) Has been cancelled
citra-build / macos (x86_64) (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-transifex / transifex (push) Has been cancelled
citra-build / macos-universal (push) Has been cancelled
Some checks failed
citra-build / source (push) Has been cancelled
citra-build / linux (appimage) (push) Has been cancelled
citra-build / linux (appimage-wayland) (push) Has been cancelled
citra-build / linux (fresh) (push) Has been cancelled
citra-build / macos (arm64) (push) Has been cancelled
citra-build / macos (x86_64) (push) Has been cancelled
citra-build / windows (msvc) (push) Has been cancelled
citra-build / windows (msys2) (push) Has been cancelled
citra-build / android (push) Has been cancelled
citra-build / docker (push) Has been cancelled
citra-format / clang-format (push) Has been cancelled
citra-transifex / transifex (push) Has been cancelled
citra-build / macos-universal (push) Has been cancelled
This commit is contained in:
parent
f7eaf13a4d
commit
109f5fb730
@ -467,6 +467,8 @@ void GSP_GPU::TriggerCmdReqQueue(Kernel::HLERequestContext& ctx) {
|
||||
void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
LOG_DEBUG(Service_GSP, "called");
|
||||
|
||||
if (active_thread_id == std::numeric_limits<u32>::max()) {
|
||||
LOG_WARNING(Service_GSP, "Called without an active thread.");
|
||||
|
||||
@ -503,28 +505,64 @@ void GSP_GPU::ImportDisplayCaptureInfo(Kernel::HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(top_entry);
|
||||
rb.PushRaw(bottom_entry);
|
||||
|
||||
LOG_WARNING(Service_GSP, "called");
|
||||
}
|
||||
|
||||
static void CopyFrameBuffer(Core::System& system, VAddr dst, VAddr src, u32 stride, u32 lines) {
|
||||
auto dst_ptr = system.Memory().GetPointer(dst);
|
||||
const auto src_ptr = system.Memory().GetPointer(src);
|
||||
static void CopyFrameBuffer(Core::System& system, VAddr dst, VAddr src, u32 dst_stride,
|
||||
u32 src_stride, u32 lines) {
|
||||
auto* dst_ptr = system.Memory().GetPointer(dst);
|
||||
const auto* src_ptr = system.Memory().GetPointer(src);
|
||||
|
||||
if (!dst_ptr || !src_ptr) {
|
||||
LOG_WARNING(Service_GSP,
|
||||
"Could not resolve pointers for framebuffer capture, skipping screen.");
|
||||
return;
|
||||
}
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(src, stride * lines, Memory::FlushMode::Flush);
|
||||
std::memcpy(dst_ptr, src_ptr, stride * lines);
|
||||
system.Memory().RasterizerFlushVirtualRegion(dst, stride * lines,
|
||||
system.Memory().RasterizerFlushVirtualRegion(src, src_stride * lines, Memory::FlushMode::Flush);
|
||||
|
||||
const u32 copy_bytes_per_line = std::min(src_stride, dst_stride);
|
||||
for (u32 y = 0; y < lines; ++y) {
|
||||
std::memcpy(dst_ptr, src_ptr, copy_bytes_per_line);
|
||||
src_ptr += src_stride;
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(dst, dst_stride * lines,
|
||||
Memory::FlushMode::Invalidate);
|
||||
}
|
||||
|
||||
static void ClearFramebuffer(Core::System& system, VAddr dst, u32 dst_stride, u32 lines) {
|
||||
auto* dst_ptr = system.Memory().GetPointer(dst);
|
||||
|
||||
if (!dst_ptr) {
|
||||
LOG_WARNING(Service_GSP,
|
||||
"Could not resolve pointers for framebuffer clear, skipping screen.");
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 set_bytes_per_line = dst_stride;
|
||||
for (u32 y = 0; y < lines; ++y) {
|
||||
std::memset(dst_ptr, 0, set_bytes_per_line);
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
|
||||
system.Memory().RasterizerFlushVirtualRegion(dst, dst_stride * lines,
|
||||
Memory::FlushMode::Invalidate);
|
||||
}
|
||||
|
||||
void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
LOG_DEBUG(Service_GSP, "called");
|
||||
|
||||
// Taken from GSP decomp. TODO: GSP seems to so something special
|
||||
// when the fb format results in bpp of 0 or 4, most likely clearing
|
||||
// it, more research needed.
|
||||
static const u8 bpp_per_format[] = {// Valid values
|
||||
4, 3, 2, 2, 2,
|
||||
// Invalid values
|
||||
0, 0, 0};
|
||||
|
||||
if (active_thread_id == std::numeric_limits<u32>::max()) {
|
||||
LOG_WARNING(Service_GSP, "Called without an active thread.");
|
||||
|
||||
@ -534,47 +572,71 @@ void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INFO(Service_GSP, "called");
|
||||
|
||||
// TODO: This should also save LCD register state.
|
||||
system.Memory().RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
||||
Memory::FlushMode::Flush);
|
||||
const auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
|
||||
saved_vram.emplace(std::vector<u8>(Memory::VRAM_SIZE));
|
||||
std::memcpy(saved_vram.get().data(), vram, Memory::VRAM_SIZE);
|
||||
|
||||
const auto top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
||||
auto top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
||||
if (top_screen) {
|
||||
u8 bytes_per_pixel =
|
||||
bpp_per_format[top_screen->framebuffer_info[top_screen->index].GetPixelFormat()];
|
||||
const auto top_fb = top_screen->framebuffer_info[top_screen->index];
|
||||
if (top_fb.address_left) {
|
||||
if (top_fb.address_left && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
||||
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_LEFT, top_fb.address_left,
|
||||
top_fb.stride, TOP_FRAMEBUFFER_HEIGHT);
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, top_fb.stride,
|
||||
TOP_FRAMEBUFFER_HEIGHT);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No framebuffer bound to top left screen, skipping capture.");
|
||||
LOG_DEBUG(Service_GSP, "Invalid framebuffer bound to top left screen, clearing...");
|
||||
ClearFramebuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_LEFT,
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, TOP_FRAMEBUFFER_HEIGHT);
|
||||
}
|
||||
if (top_fb.address_right) {
|
||||
if (top_fb.address_right && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
||||
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_RIGHT, top_fb.address_right,
|
||||
top_fb.stride, TOP_FRAMEBUFFER_HEIGHT);
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, top_fb.stride,
|
||||
TOP_FRAMEBUFFER_HEIGHT);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No framebuffer bound to top right screen, skipping capture.");
|
||||
LOG_DEBUG(Service_GSP, "Invalid framebuffer bound to top right screen, clearing...");
|
||||
ClearFramebuffer(system, FRAMEBUFFER_SAVE_AREA_TOP_RIGHT,
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, TOP_FRAMEBUFFER_HEIGHT);
|
||||
}
|
||||
|
||||
FrameBufferInfo fb_info = top_screen->framebuffer_info[top_screen->index];
|
||||
|
||||
fb_info.address_left = FRAMEBUFFER_SAVE_AREA_TOP_LEFT;
|
||||
fb_info.address_right = FRAMEBUFFER_SAVE_AREA_TOP_RIGHT;
|
||||
fb_info.stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
||||
system.GPU().SetBufferSwap(0, fb_info);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No top screen bound, skipping capture.");
|
||||
}
|
||||
|
||||
const auto bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
|
||||
auto bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
|
||||
if (bottom_screen) {
|
||||
u8 bytes_per_pixel =
|
||||
bpp_per_format[bottom_screen->framebuffer_info[bottom_screen->index].GetPixelFormat()];
|
||||
const auto bottom_fb = bottom_screen->framebuffer_info[bottom_screen->index];
|
||||
if (bottom_fb.address_left) {
|
||||
if (bottom_fb.address_left && bytes_per_pixel != 0 && bytes_per_pixel != 4) {
|
||||
CopyFrameBuffer(system, FRAMEBUFFER_SAVE_AREA_BOTTOM, bottom_fb.address_left,
|
||||
bottom_fb.stride, BOTTOM_FRAMEBUFFER_HEIGHT);
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, bottom_fb.stride,
|
||||
BOTTOM_FRAMEBUFFER_HEIGHT);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No framebuffer bound to bottom screen, skipping capture.");
|
||||
LOG_DEBUG(Service_GSP, "Invalid framebuffer bound to bottom screen, clearing...");
|
||||
ClearFramebuffer(system, FRAMEBUFFER_SAVE_AREA_BOTTOM,
|
||||
FRAMEBUFFER_WIDTH * bytes_per_pixel, BOTTOM_FRAMEBUFFER_HEIGHT);
|
||||
}
|
||||
FrameBufferInfo fb_info = bottom_screen->framebuffer_info[bottom_screen->index];
|
||||
|
||||
fb_info.address_left = FRAMEBUFFER_SAVE_AREA_BOTTOM;
|
||||
fb_info.stride = FRAMEBUFFER_WIDTH * bytes_per_pixel;
|
||||
system.GPU().SetBufferSwap(1, fb_info);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No bottom screen bound, skipping capture.");
|
||||
}
|
||||
|
||||
// Real GSP waits for VBlank here, but we don't need it (?).
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
@ -582,16 +644,31 @@ void GSP_GPU::SaveVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
void GSP_GPU::RestoreVramSysArea(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
LOG_INFO(Service_GSP, "called");
|
||||
LOG_DEBUG(Service_GSP, "called");
|
||||
|
||||
if (saved_vram) {
|
||||
// TODO: This should also restore LCD register state.
|
||||
auto vram = system.Memory().GetPointer(Memory::VRAM_VADDR);
|
||||
std::memcpy(vram, saved_vram.get().data(), Memory::VRAM_SIZE);
|
||||
system.Memory().RasterizerFlushVirtualRegion(Memory::VRAM_VADDR, Memory::VRAM_SIZE,
|
||||
Memory::FlushMode::Invalidate);
|
||||
}
|
||||
|
||||
auto top_screen = GetFrameBufferInfo(active_thread_id, 0);
|
||||
if (top_screen) {
|
||||
system.GPU().SetBufferSwap(0, top_screen->framebuffer_info[top_screen->index]);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No top screen bound, skipping restore.");
|
||||
}
|
||||
|
||||
auto bottom_screen = GetFrameBufferInfo(active_thread_id, 1);
|
||||
if (bottom_screen) {
|
||||
system.GPU().SetBufferSwap(1, bottom_screen->framebuffer_info[top_screen->index]);
|
||||
} else {
|
||||
LOG_WARNING(Service_GSP, "No bottom screen bound, skipping restore.");
|
||||
}
|
||||
|
||||
// Real GSP waits for VBlank here, but we don't need it (?).
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
@ -30,6 +30,8 @@ class SharedMemory;
|
||||
namespace Service::GSP {
|
||||
|
||||
struct FrameBufferInfo {
|
||||
static constexpr u32 PIXEL_FORMAT_MASK = 0x7;
|
||||
|
||||
u32 active_fb; // 0 = first, 1 = second
|
||||
u32 address_left;
|
||||
u32 address_right;
|
||||
@ -37,6 +39,10 @@ struct FrameBufferInfo {
|
||||
u32 format; // maps to 0x1EF00X70 ?
|
||||
u32 shown_fb; // maps to 0x1EF00X78 ?
|
||||
u32 unknown;
|
||||
|
||||
u32 GetPixelFormat() {
|
||||
return format & PIXEL_FORMAT_MASK;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(FrameBufferInfo) == 0x1c, "Struct has incorrect size");
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user