SaveFrame's readback staging buffer was allocated HOST_VISIBLE |
HOST_COHERENT, whose first matching type is write-combined (uncached) on
most discrete GPUs. The CPU then reads the entire frame back from it —
and reads from write-combined memory are pathologically slow.
Select a HOST_CACHED type instead (preferring a cached-coherent type
when one exists), falling back to any HOST_VISIBLE type if no cached type
is advertised (#59 mechanism). When the chosen type is cached-but-not-
coherent, invalidate the mapped range before the CPU read — the read-path
counterpart of FlushDevice (#60) — gated on the chosen type's actual
coherency, not the requested flags. Whole-range invalidate sidesteps
nonCoherentAtomSize.
Deliberately not routed through the #89 upload-strategy helper, which is
upload-only and steers toward write-combined DEVICE_LOCAL|HOST_VISIBLE —
the worst type for readback.
Verified by driving SaveFrame from the VulkanTriangle example: the
barycentric RGB triangle reads back with correct pixels and channel
order on an RTX 4090.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>