[Gpu] Stop retrying array uploads that overrun their allocation (#476)

A 2D-array texture whose Depth times the per-slice stride runs past its
real allocation fails a slice read partway through the upload loop, and
falls through to the single-slice path after already detiling the layers
it did read. That fall-through builds the texture with ArrayLayers
defaulting to 1, so the presenter caches it under a one-layer key while
the next draw looks it up with ArrayLayers = Depth. The two never match,
so the texture misses the cache and repeats the whole read-and-detile on
every draw, throwing the result away each time.

Detiling is per-texel swizzle math, so one such texture retried a few
times per frame is expensive: it measured 568-879 ms of every second in
Demon's Souls, against a 1.4 second frame.

An allocation that is too short stays too short, so remembering the
address and not retrying it costs nothing and repairs the cache key as a
side effect: with the array upload skipped, arrayUploadLayers is 1, which
is exactly what the fall-through texture reports.

Tested on Demon's Souls (PPSA01342): 0.7 fps to 3.6-4.1 fps, CPU detile
time per second from ~700 ms to 0, and arrayed textures go from missing
the cache on every draw to hitting it every time. 28 of the 29 array
uploads in that run already succeeded and are unaffected; only the one
overrunning texture now falls back to its base slice. 495 tests pass.
This commit is contained in:
TarkusTK
2026-07-20 18:22:19 +02:00
committed by GitHub
parent ac883e44fa
commit 224a36eba7
+5 -1
View File
@@ -221,6 +221,7 @@ public static partial class AgcExports
(ulong Es, ulong State, ulong AliasAlignment),
IGuestCompiledShader> _depthOnlyVertexShaderCache = new();
private static readonly Dictionary<ulong, ulong> _shaderHeadersByCode = new();
private static readonly ConcurrentDictionary<ulong, byte> _arrayUploadUnsupported = new();
private static readonly bool _traceAgc = string.Equals(
Environment.GetEnvironmentVariable("SHARPEMU_LOG_AGC"),
"1",
@@ -7984,7 +7985,8 @@ public static partial class AgcExports
descriptor.Address != 0 &&
(descriptor.Type == Gen5TextureType2DArray ||
descriptor.Type == Gen5TextureType1DArray) &&
descriptor.Depth > 1;
descriptor.Depth > 1 &&
!_arrayUploadUnsupported.ContainsKey(descriptor.Address);
var arrayUploadLayers = wantsArrayUpload ? descriptor.Depth : 1u;
// Upload-known (not plain availability): the presenter's answer goes
@@ -8213,6 +8215,8 @@ public static partial class AgcExports
return true;
}
}
_arrayUploadUnsupported.TryAdd(descriptor.Address, 0);
}
var source = new byte[(int)physicalSourceByteCount];