From f53f0ca4833b5e99ffe53973e1cc21a34346d98f Mon Sep 17 00:00:00 2001 From: nmzik Date: Sun, 27 Sep 2026 12:55:10 +0400 Subject: [PATCH] Graphics: preserve rendered contents across 1D and 2D texture aliases --- .../host_gpu/renderer/cache/textureCache.cpp | 24 ++++++---- tests/ShaderRecompilerComputeTests.cpp | 44 +++++++++++++++++++ 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index 638078e70..f09bef252 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -193,7 +193,7 @@ bool TextureCache::SameBacking(const ImageInfo& cached, const ImageInfo& request return false; } if (!ImageViewOps::FormatsCompatible(cached.pixel_format, requested.pixel_format) || - (cached.type != requested.type && requested.extent != vk::Extent3D {1, 1, 1})) { + cached.type != requested.type) { return false; } if (exact_format && cached.pixel_format != requested.pixel_format) { @@ -639,9 +639,12 @@ void TextureCache::CopyImage(ImageId destination_id, ImageId source_id) { const bool source_depth = source.info.IsDepth(); const bool dest_depth = destination.info.IsDepth(); const bool direct_copy = - source.backing.format == destination.backing.format || - (!source_depth && !dest_depth && - vk::blockSize(source.backing.format) == vk::blockSize(destination.backing.format)); + (source.backing.image_type == destination.backing.image_type || + (source.backing.image_type != vk::ImageType::e1D && + destination.backing.image_type != vk::ImageType::e1D)) && + (source.backing.format == destination.backing.format || + (!source_depth && !dest_depth && + vk::blockSize(source.backing.format) == vk::blockSize(destination.backing.format))); if (direct_copy) { destination.CopyImage(source); } else if (!CopyD16(destination, source)) { @@ -810,12 +813,15 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques return {merged_id}; } // PPSA08394 + // A view cannot change the native image type or grow its extent. if (requested.data.size == cached.info.data.size && - requested.resources == cached.info.resources && requested.type == cached.info.type && - requested.extent.width > cached.info.extent.width && - requested.extent.height >= cached.info.extent.height && - requested.extent.depth >= cached.info.extent.depth && - ImageViewOps::FormatsCompatible(cached.info.pixel_format, requested.pixel_format)) { + requested.resources == cached.info.resources && + ImageViewOps::FormatsCompatible(cached.info.pixel_format, requested.pixel_format) && + (requested.type != cached.info.type + ? requested.extent == cached.info.extent + : requested.extent.width > cached.info.extent.width && + requested.extent.height >= cached.info.extent.height && + requested.extent.depth >= cached.info.extent.depth)) { return {ExpandImage(requested, cached_id)}; } // PS5 mip tails can expose more levels without increasing the guest allocation. diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index a2f4cf8bc..2b9237e70 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -8478,6 +8478,50 @@ public: {1, 1, 1}, 0, mip) == expected, "the sampled target lost its GPU-written final texel"); } + if (width == 1 && height == 1) { + for (const bool uint_view : {true, false}) { + descriptor.fields[1] = static_cast( + uint_view ? Prospero::BufferFormat::k16_16_16_16UInt + : Prospero::BufferFormat::k16_16_16_16Float) << 20u; + descriptor.fields[3] = 0x81b00facu; // Captured 1D view, tile27, RGBA swizzle. + std::copy_n(descriptor.fields, 8, value.dwords.begin()); + resource.numeric_class = uint_view ? Prospero::TextureNumericClass::Uint + : Prospero::TextureNumericClass::Float; + resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim1D; + const auto one_d = + RenderExecutorTestAccess::ResolveTexture(executor, resource, value); + const auto one_d_view = texture_cache.FindTexture(one_d.image_id, one_d.desc); + const auto repeated = + RenderExecutorTestAccess::ResolveTexture(executor, resource, value); + Require(name, "1D sampled backing and reuse", + one_d.image_id != color.image_id && one_d_view != nullptr && + one_d.desc.view_info.type == vk::ImageViewType::e1D && + texture_cache.GetImage(one_d.image_id).backing.image_type == + vk::ImageType::e1D && + repeated.image_id == one_d.image_id && + texture_cache.FindTexture(repeated.image_id, repeated.desc) == one_d_view && + ReadCachedTexel(name, context, one_d.image_id) == expected, + "the 1D alias reused an incompatible backing or lost the rendered texel"); + + RenderExecutorTestAccess::ResetBindings(executor); + RenderExecutorTestAccess::ResolveRenderColorTarget( + executor, scheduler.Current(), color, 0); + const auto restored = RenderExecutorTestAccess::AcquireRenderTargets( + executor, scheduler.Current(), &color, 1, no_depth); + scheduler.Current().BeginRendering(restored); + scheduler.Current().EndRendering(); + Require(name, "2D target restoration", + color.image_id != one_d.image_id && + restored.color_attachments[0].image_view != nullptr && + texture_cache.GetImage(color.image_id).backing.image_type == + vk::ImageType::e2D && + texture_cache.GetImage(color.image_id).info.pixel_format == + vk::Format::eR16G16B16A16Sfloat && + ReadCachedTexel(name, context, color.image_id) == expected, + "restoring the 2D target lost its view type or GPU-written texel"); + RenderExecutorTestAccess::ResetBindings(executor); + } + } } RenderExecutorTestAccess::ResetBindings(executor);