From e65b2e823324aa3afc7c4dfb2b6ee55067491adf Mon Sep 17 00:00:00 2001 From: kuba Date: Sat, 3 Oct 2026 19:37:33 +0200 Subject: [PATCH] [Gpu] Cover the whole tile plan when a buffer reads back an image TrySynchronizeBufferFromImage sized the copy from the mip layout, but the tile plan of a fully fitting image can extend past it; Silent Hill hit a 128x128 slice planned at the exact end of the mip chain and the tiler aborted the process. Grow the copy to the plan's tiled end when the buffer holds it, otherwise keep the guest bytes. --- .../Gpu/Images/GuestImageCache.Transfers.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/SharpEmu.Libs/Gpu/Images/GuestImageCache.Transfers.cs b/src/SharpEmu.Libs/Gpu/Images/GuestImageCache.Transfers.cs index 000513d..d4b8cc9 100644 --- a/src/SharpEmu.Libs/Gpu/Images/GuestImageCache.Transfers.cs +++ b/src/SharpEmu.Libs/Gpu/Images/GuestImageCache.Transfers.cs @@ -703,11 +703,39 @@ public sealed unsafe partial class GuestImageCache } } + // The mip layout can end before the tile plan does (Silent Hill: a 128x128 slice planned at + // the exact end of the mip chain aborted the process). Cover the whole plan when the buffer + // holds it; otherwise keep the guest bytes instead of copying a partial image. + if (!plan.Depth && plan.Color.Tiled) + { + var tiledEnd = TiledEndOf(plan.Color.Tiles); + if (tiledEnd > copySize) + { + if (tiledEnd > available) + { + return false; + } + + copySize = tiledEnd; + } + } + DownloadToBuffer(image, buffer, bufferOffset, copySize, plan); image.MarkBufferHoldsGpuContents(); return true; } + private static ulong TiledEndOf(List tiles) + { + var end = 0UL; + foreach (var tile in tiles) + { + end = Math.Max(end, tile.TiledOffset + tile.TiledSize); + } + + return end; + } + // Publishes a GPU-owned image to guest memory after its tick completes; false when it cannot. private bool TryDownloadToGuest(ResourceSlotIdentifier imageIdentifier) {