diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index b16d257d9..e2a779d79 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -804,8 +804,14 @@ TextureCache::OverlapResult TextureCache::ResolveOverlap(const ImageInfo& reques if (requested.IsBlock() && !cached.info.IsBlock()) { return {ExpandImage(requested, cached_id)}; } - if (requested.data.size == cached.info.data.size && - (requested.IsVolume() || cached.info.IsVolume())) { + // Volume depth is not an array-layer count. A larger depth can retain the + // same block-slice layout while requiring a larger native image. + if ((requested.IsVolume() || cached.info.IsVolume()) && + (requested.data.size == cached.info.data.size || + (requested.type == cached.info.type && requested.resources == cached.info.resources && + 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)}; } // PPSA08394 diff --git a/src/graphics/host_gpu/renderer/image/image.cpp b/src/graphics/host_gpu/renderer/image/image.cpp index 77ecdb835..7f35de2e6 100644 --- a/src/graphics/host_gpu/renderer/image/image.cpp +++ b/src/graphics/host_gpu/renderer/image/image.cpp @@ -340,7 +340,9 @@ void Image::CopyImage(Image& source) { EXIT_IF(source.backing.samples != backing.samples); m_scheduler.EndRendering(); const uint32_t levels = std::min(source.backing.mip_levels, backing.mip_levels); - const uint32_t base_depth = backing.image_type == vk::ImageType::e3D + const uint32_t base_depth = source.backing.image_type == backing.image_type + ? std::min(source.backing.extent.depth, backing.extent.depth) + : backing.image_type == vk::ImageType::e3D ? backing.extent.depth : source.backing.extent.depth; const auto source_aspect = diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 2b4ffa7a2..86e1ad205 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -9111,9 +9111,10 @@ public: void CheckRenderExecutorColorVolumeDiscovery() { constexpr const char *name = "RenderExecutorColorVolumeDiscovery"; constexpr uintptr_t base = 0x0000000203e00000ull; + constexpr uint64_t slice_size = 0x10000; constexpr uint64_t color_size = 0x200000; constexpr uint64_t metadata_size = 0x20000; - constexpr uint64_t allocation_size = color_size + metadata_size; + constexpr uint64_t allocation_size = 56 * slice_size; constexpr uint64_t allocation_alignment = 0x10000; EnsureRuntimeContext(); @@ -9131,7 +9132,6 @@ public: mapped == reinterpret_cast(base), "color-volume fixed mapping failed"); std::memset(mapped, 0x5a, allocation_size); - constexpr uint64_t slice_size = 0x10000; { RenderContext context(m_runtime_context); @@ -9358,6 +9358,26 @@ public: [](uint8_t byte) { return byte == 0xff; }), "full volume acquisition left an unconsumed native clear slice"); + TextureCacheTestAccess::ClearImage( + texture_cache, scheduler.Current(), color.image_id, + {vk::ImageAspectFlagBits::eColor, 0, 1, 31, 1}, painted); + auto expanded = storage_desc; + expanded.info.extent.depth = 56; + expanded.info.data.size = allocation_size; + const auto expanded_id = texture_cache.FindImage(expanded); + Require(name, "3D storage depth expansion", + expanded_id != color.image_id && + !TextureCacheTestAccess::Contains(texture_cache, color.image_id) && + texture_cache.GetImage(expanded_id).backing.extent.depth == 56 && + texture_cache.GetImage(expanded_id).IsGpuModified(), + "a deeper storage binding reused the smaller native volume"); + Require(name, "3D expansion preserves native prefix and guest suffix", + ReadCachedTexel(name, context, expanded_id, {0, 0, 31}) == + std::vector{0x3fffffffu} && + ReadCachedTexel(name, context, expanded_id, {0, 0, 55}) == + std::vector{0x5a5a5a5a}, + "volume expansion lost GPU writes or copied beyond the old depth"); + RenderExecutorTestAccess::ResetBindings(executor); resources.UnmapMemory(base, allocation_size); scheduler.Finish();