From 190608ae20e8f1607af5339d3f37f8595248461c Mon Sep 17 00:00:00 2001 From: nmzik Date: Sat, 3 Oct 2026 07:20:20 +0400 Subject: [PATCH] Publish consumed color metadata without invalidating pooled images --- .../host_gpu/renderer/cache/textureCache.cpp | 8 ++- tests/ShaderRecompilerComputeTests.cpp | 58 +++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index b36bc34a5..b4457670a 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -1169,10 +1169,12 @@ void TextureCache::MaterializeColorClear(ImageId id, const ImageDesc& desc, {vk::ImageAspectFlagBits::eColor, view.base_level, view.level_count, image_first + slice, 1}, clear); } - // Native expanded keys own consumption. Existing buffer tracking publishes this CPU - // write to future GPU readers; FillBuffer can fault and must run outside the texture lock. + // Publish the conversion's expanded keys without treating them as guest writes + // to overlapping image data. Invalidate the buffer before updating its backing. if (desc.type != BindingType::VideoOut) { - m_buffer_cache.FillBuffer(address, slice_size, UINT32_MAX, false); + std::fill(bytes.begin(), bytes.end(), uint8_t {0xff}); + m_buffer_cache.InvalidateMemory(address, slice_size); + LibKernel::Memory::WriteBacking(address, bytes.data(), bytes.size()); } } } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 1aaba7ccc..bfc8970e8 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -9861,6 +9861,64 @@ public: read_texel() == expected, "completing the metadata overwrite did not restore its clear"); + // Consuming a clear key updates metadata backing without invalidating + // a pooled image whose first texel precedes the metadata range. + constexpr uint32_t alias_value = 0x13579bdfu; + constexpr uint32_t expanded_words = 0x1000 / sizeof(uint32_t); + const auto selected_metadata = dcc_address + metadata_size - 0x1000; + ImageDesc metadata_alias{}; + metadata_alias.info.data = {dcc_address - 0x1000, 0x4000}; + metadata_alias.info.pixel_format = vk::Format::eR32Uint; + metadata_alias.info.guest_format = Prospero::BufferFormat::k32UInt; + metadata_alias.info.extent = {4096, 1, 1}; + metadata_alias.info.pitch = 4096; + metadata_alias.info.bytes_per_block = sizeof(uint32_t); + metadata_alias.info.mip_layout[0] = {0, 0x4000, 4096, 1}; + metadata_alias.view_info.format = vk::Format::eR32Uint; + const auto metadata_alias_id = texture_cache.FindImage(metadata_alias); + vk::ClearValue alias_paint{}; + alias_paint.color.uint32[0] = alias_value; + TextureCacheTestAccess::ClearImage( + texture_cache, scheduler.Current(), metadata_alias_id, + {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}, alias_paint); + paint(); + fill_metadata(metadata_words); + bind(); + Require(name, "pooled metadata clear target", read_texel() == expected, + "the overlapping native image prevented the metadata clear"); + + auto metadata_source = resources.GetBufferCache().ObtainBuffer( + selected_metadata, 0x1000, false); + Require(name, "expanded metadata buffer source", metadata_source.first != nullptr, + "expanded metadata did not produce a GPU-readable buffer"); + auto metadata_readback = CreateHostBuffer( + name, 0x1000, vk::BufferUsageFlagBits::eTransferDst, {}); + vk::MemoryBarrier metadata_barrier{}; + metadata_barrier.srcAccessMask = vk::AccessFlagBits::eMemoryWrite; + metadata_barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead; + scheduler.Current().Handle().pipelineBarrier( + vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer, + {}, 1, &metadata_barrier, 0, nullptr, 0, nullptr); + const vk::BufferCopy metadata_copy{metadata_source.second, 0, 0x1000}; + scheduler.Current().Handle().copyBuffer(metadata_source.first->Handle(), + metadata_readback.buffer, 1, &metadata_copy); + metadata_barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite; + metadata_barrier.dstAccessMask = vk::AccessFlagBits::eHostRead; + scheduler.Current().Handle().pipelineBarrier( + vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eHost, + {}, 1, &metadata_barrier, 0, nullptr, 0, nullptr); + scheduler.Finish(); + Require(name, "expanded metadata GPU reader", + ReadBuffer(name, metadata_readback, expanded_words) == + std::vector(expanded_words, UINT32_MAX), + "a later GPU buffer read retained the consumed clear key"); + DestroyBuffer(&metadata_readback); + (void)texture_cache.FindTexture(metadata_alias_id, metadata_alias); + Require(name, "metadata consumption preserves pooled image", + ReadCachedTexel(name, context, metadata_alias_id) == + std::vector{alias_value}, + "internal metadata publication replaced an untouched GPU image texel"); + ImageDesc previous_depth{}; previous_depth.type = BindingType::DepthTarget; previous_depth.info.data = {base + 0x180000, 0x10000};