From d8c4a9af3850254613cd1ab3c000495c35038981 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 17 Oct 2026 17:31:25 +0200 Subject: [PATCH] graphics: preserve HTILE metadata across native image replacement --- .../host_gpu/renderer/cache/textureCache.cpp | 10 +------- tests/ShaderRecompilerComputeTests.cpp | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index 6e173b09a..e7e9956b1 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -263,15 +263,6 @@ void TextureCache::DeleteImage(ImageId id) { EXIT("TextureCache: deleting a GPU-modified image without resolving its contents\n"); } m_download_images.erase(id); - if (image->info.HasMetadata()) { - const auto metadata = m_surface_metas.find(image->info.metadata.range.address); - if (metadata != m_surface_metas.end() && - image->info.metadata.kind == ImageMetadataKind::Htile && - metadata->second.type == MetaDataInfo::Type::HTile) { - // A later binding may have reused this address for another metadata type. - m_surface_metas.erase(metadata); - } - } UnregisterImage(id); if (m_scheduler.Active()) { m_scheduler.DeferOperation([this, id] { m_slot_images.erase(id); }); @@ -1885,6 +1876,7 @@ void TextureCache::UnmapMemory(uint64_t address, uint64_t size) { EXIT("TextureCache: invalid unmap range\n"); } std::scoped_lock lock {m_lock}; + // HTILE belongs to guest memory, independently of native image replacements. for (auto metadata = m_surface_metas.begin(); metadata != m_surface_metas.end();) { const auto base = metadata->first; if (base >= address && base < address + size) { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index fec5f7279..4aa574598 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -6572,6 +6572,11 @@ public: htile_depth.view_info.aspect = vk::ImageAspectFlagBits::eDepth; htile_depth.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment; const auto htile_depth_image = texture_cache.FindImage(htile_depth); + (void)texture_cache.FindDepthTarget(htile_depth_image, htile_depth); + Require(name, "HTile depth metadata registration", + texture_cache.ClearMeta(htile_depth.info.metadata.range.address) && + texture_cache.TouchMeta(htile_depth.info.metadata.range.address, 0, false), + "depth target did not register independent slice clear state"); vk::ClearValue htile_clear{}; htile_clear.depthStencil.depth = 0.25f; TextureCacheTestAccess::ClearImage( @@ -6605,6 +6610,11 @@ public: Require(name, "HTile depth storage prefix view", texture_cache.FindTexture(id, htile_storage) != nullptr, "converted depth prefix has no storage view"); + Require(name, "HTile metadata survives native depth replacement", + texture_cache.IsMeta(htile_depth.info.metadata.range.address) && + !texture_cache.IsMetaCleared(htile_depth.info.metadata.range.address, 0) && + texture_cache.IsMetaCleared(htile_depth.info.metadata.range.address, 1), + "storage conversion discarded guest metadata or its slice clear state"); htile_storage_image = id; } auto htile_readback = CreateHostBuffer( @@ -6625,6 +6635,11 @@ public: std::vector(htile_layers, std::bit_cast(0.25f)), "partial storage conversion lost native depth data outside its view"); DestroyBuffer(&htile_readback); + texture_cache.UnmapMemory(htile_depth.info.metadata.range.address, + htile_depth.info.metadata.range.size); + Require(name, "HTile metadata allocation unmap", + !texture_cache.IsMeta(htile_depth.info.metadata.range.address), + "unmapped guest metadata retained its registration"); auto layered_stencil_desc = MakeLinearDesc( base + 0x27d4000, 6 * 4 * sizeof(float), @@ -7898,9 +7913,16 @@ public: "retiring the old DCC image erased the live HTile slice state"); texture_cache.UnmapMemory(reused_depth.info.data.address, reused_depth.info.data.size); + Require(name, "HTile allocation outlives native depth retirement", + texture_cache.IsMeta(base + 0x28000) && + !texture_cache.IsMetaCleared(base + 0x28000, 0) && + texture_cache.IsMetaCleared(base + 0x28000, 1), + "retiring depth data erased an independent metadata allocation"); + texture_cache.UnmapMemory(reused_depth.info.metadata.range.address, + reused_depth.info.metadata.range.size); Require(name, "reused metadata final retirement", !texture_cache.IsMeta(base + 0x28000), - "retiring the HTile image left its metadata registered"); + "unmapping the HTile allocation left its metadata registered"); constexpr uint64_t partial_unmap_image_offset = 0x2700000; auto partial_unmap_image =