From 6ca820198257e9482b182712daff12476f6706e7 Mon Sep 17 00:00:00 2001 From: nmzik Date: Sun, 6 Sep 2026 10:34:02 +0400 Subject: [PATCH] textures: handle DCC memory reused as HTile --- .../host_gpu/renderer/cache/textureCache.cpp | 24 +++++---- tests/ShaderRecompilerComputeTests.cpp | 50 +++++++++++++++++++ 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index 5ea1da1df..c5c5cd043 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -328,7 +328,15 @@ void TextureCache::DeleteImage(ImageId id) { } m_download_images.erase(id); if (image->info.HasMetadata()) { - m_surface_metas.erase(image->info.metadata.range.address); + const auto metadata = m_surface_metas.find(image->info.metadata.range.address); + if (metadata != m_surface_metas.end() && + ((image->info.metadata.kind == ImageMetadataKind::Dcc && + metadata->second.type == MetaDataInfo::Type::Dcc) || + (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()) { @@ -1436,15 +1444,11 @@ vk::ImageView TextureCache::FindDepthTarget(ImageId id, const ImageDesc& desc) { m_surface_metas.try_emplace(desc.info.metadata.range.address, MetaDataInfo {.type = MetaDataInfo::Type::HTile, .clear_mask = image.info.htile_clear_mask}); - if (!inserted && metadata->second.type == MetaDataInfo::Type::PendingDcc) { - // PendingDcc fills use DCC-specific encoding. Do not reinterpret a speculative - // DCC/buffer fill as HTile state if the address is later classified as depth metadata. - metadata->second.type = MetaDataInfo::Type::HTile; - metadata->second.clear_mask = image.info.htile_clear_mask; - metadata->second.fill_value = 0xffffffffu; - metadata->second.fill_size = 0; - } else if (!inserted && metadata->second.type != MetaDataInfo::Type::HTile) { - EXIT("TextureCache: depth target reuses non-HTile metadata\n"); + if (!inserted && metadata->second.type != MetaDataInfo::Type::HTile) { + // PS5 allocations can reuse DCC storage as HTile while the old color image is cached. + // The depth binding defines the new type; incompatible fill state cannot carry over. + metadata->second = {.type = MetaDataInfo::Type::HTile, + .clear_mask = image.info.htile_clear_mask}; } } CommitGpuWrite(image); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index b9e1f3e9a..498dccf65 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -6025,6 +6025,56 @@ public: texture_cache.IsMetaCleared(base + metadata_b, 0), "BufferCache fill did not publish an exact-address metadata clear"); + // R-Type Final 3 reuses an old color allocation inside a new stencil plane, + // with the same metadata address changing from DCC to HTile. + auto reused_depth = MakeMetadataDepth(0x12300, 0x13200); + reused_depth.info.pixel_format = vk::Format::eD32SfloatS8Uint; + reused_depth.info.stencil = {base + 0x12200, 0x80}; + reused_depth.info.htile_clear_mask = 0; + reused_depth.view_info.format = vk::Format::eD32SfloatS8Uint; + reused_depth.view_info.aspect = vk::ImageAspectFlagBits::eDepth | + vk::ImageAspectFlagBits::eStencil; + auto reused_color = MakeLinearDesc( + base + 0x12240, sizeof(uint32_t), vk::Format::eR8G8B8A8Unorm, + Prospero::BufferFormat::k8_8_8_8UNorm, Prospero::ImageType::kColor2D, + {1, 1, 1}, 1, 4, 1); + reused_color.type = BindingType::RenderTarget; + reused_color.info.metadata.kind = ImageMetadataKind::Dcc; + reused_color.info.metadata.range = {base + 0x13200, 0x20}; + reused_color.view_info.usage = vk::ImageUsageFlagBits::eColorAttachment; + const auto reused_color_id = texture_cache.FindImage(reused_color); + (void)texture_cache.FindRenderTarget(reused_color_id, reused_color); + texture_cache.TrackDccFill(base + 0x13200, 0x80, 0); + texture_cache.InvalidateMemoryFromGPU(reused_depth.info.stencil.address, + reused_depth.info.stencil.size); + const auto reused_depth_id = texture_cache.FindImage(reused_depth); + (void)texture_cache.FindDepthTarget(reused_depth_id, reused_depth); + uint32_t reused_fill = 0; + Require( + name, "DCC allocation reused as HTile", + TextureCacheTestAccess::Contains(texture_cache, reused_color_id) && + texture_cache.GetImage(reused_color_id).IsBufferModified() && + !texture_cache.GetImage(reused_color_id).IsGpuModified() && + !texture_cache.IsMetaCleared(base + 0x13200, 0, &reused_fill) && + reused_fill == UINT32_MAX && + texture_cache.ClearMeta(base + 0x13200) && + texture_cache.TouchMeta(base + 0x13200, 0, false), + "depth binding retained incompatible DCC clear state"); + TextureCacheTestAccess::DeleteImage(texture_cache, reused_color_id); + (void)texture_cache.FindDepthTarget(reused_depth_id, reused_depth); + Require( + name, "reused metadata owner retirement", + !TextureCacheTestAccess::Contains(texture_cache, reused_color_id) && + texture_cache.IsMeta(base + 0x13200) && + !texture_cache.IsMetaCleared(base + 0x13200, 0) && + texture_cache.IsMetaCleared(base + 0x13200, 1), + "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, "reused metadata final retirement", + !texture_cache.IsMeta(base + 0x13200), + "retiring the HTile image left its metadata registered"); + constexpr uint64_t partial_unmap_image_offset = 0x2700000; auto partial_unmap_image = MakeLinearDesc(base + partial_unmap_image_offset, 0x2000,