diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index 5dab5ffd1..4180a3d9f 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace Libs::Graphics { @@ -251,14 +252,11 @@ void TextureCache::DeleteImage(ImageId id) { if (image == nullptr || !image->registered) { return; } - if (!image->depth_id) { - std::vector associations; - m_slot_images.ForEach([&](ImageId candidate, const Image& associated) { - if (associated.depth_id == id) { - associations.push_back(candidate); - } - }); - for (const auto association: associations) { + if (image->depth_id) { + std::erase(m_slot_images[image->depth_id].stencil_ids, id); + } else { + // Detach first: deleting each association also unlinks it from its depth owner. + for (const auto association: std::exchange(image->stencil_ids, {})) { FreeImage(association); } } @@ -1205,6 +1203,12 @@ ImageId TextureCache::AssociateStencil(ImageId depth_id, GuestRange stencil) { plane.IsTiled() ? TileGetDepthPitch(plane.extent.width, 1, 0) : depth.info.pitch; plane.bytes_per_block = 1; plane.UpdateSize(); + if (record.depth_id != depth_id) { + if (record.depth_id) { + std::erase(m_slot_images[record.depth_id].stencil_ids, association); + } + depth.stencil_ids.push_back(association); + } record.info = plane; record.depth_id = depth_id; record.stencil_subresources = depth.stencil_subresources; diff --git a/src/graphics/host_gpu/renderer/image/image.h b/src/graphics/host_gpu/renderer/image/image.h index 442bedcf8..f45009495 100644 --- a/src/graphics/host_gpu/renderer/image/image.h +++ b/src/graphics/host_gpu/renderer/image/image.h @@ -149,6 +149,8 @@ public: uint64_t track_addr = 0; uint64_t track_addr_end = 0; ImageId depth_id {}; + // Stencil associations that refer to this depth image. + std::vector stencil_ids; // The current stencil plane's mapping into the depth image, also retained by its association. ImageSubresourceRange stencil_subresources; uint64_t tick_accessed_last = 0; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index c0a961d8d..bae331f1c 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -8240,9 +8240,11 @@ public: texture_cache.UnmapMemory(ms_depth_desc.info.data.address, ms_depth_desc.info.data.size); Require(name, "depth unmap proxy retirement", - !texture_cache.FindImageFromRange(base + second_stencil_offset, - ms_stencil_size, false), - "unmapping depth retained its re-associated stencil proxy"); + !texture_cache.FindImageFromRange(base + ms_stencil_offset, + ms_stencil_size, false) && + !texture_cache.FindImageFromRange(base + second_stencil_offset, + ms_stencil_size, false), + "unmapping depth retained one of its stencil proxies"); auto reused_stencil_color = MakeLinearDesc( base + 0x2200000, 0x40000, vk::Format::eR8Unorm, @@ -8297,8 +8299,31 @@ public: ReadCachedTexel(name, context, reused_stencil_color_id, {}, {4, 1, 1}) == std::vector{0xffffffffu}, "stencil address reuse changed the native R8 clear contents"); + auto successor_depth = reused_stencil_depth; + successor_depth.info.data.address += successor_depth.info.data.size; + const auto successor_depth_id = texture_cache.FindImage(successor_depth); + TextureCacheTestAccess::AssociateStencil( + texture_cache, successor_depth_id, successor_depth.info.stencil); texture_cache.UnmapMemory(reused_stencil_depth.info.data.address, reused_stencil_depth.info.data.size); + Require(name, "reparented stencil survives old depth retirement", + !TextureCacheTestAccess::Contains(texture_cache, + reused_stencil_depth_id) && + TextureCacheTestAccess::Contains(texture_cache, + successor_depth_id) && + texture_cache.FindImageFromRange( + successor_depth.info.stencil.address, + successor_depth.info.stencil.size, false) == + reused_stencil_proxy && + texture_cache.GetImage(reused_stencil_proxy).depth_id == + successor_depth_id, + "retiring the previous depth owner removed its reparented proxy"); + texture_cache.UnmapMemory(successor_depth.info.data.address, + successor_depth.info.data.size); + Require(name, "reparented stencil retires with new depth", + !TextureCacheTestAccess::Contains(texture_cache, + reused_stencil_proxy), + "retiring the new depth owner retained its reparented proxy"); texture_cache.UnmapMemory(reused_stencil_color.info.data.address, reused_stencil_color.info.data.size);