diff --git a/src/graphics/host_gpu/renderer/bufferCache.cpp b/src/graphics/host_gpu/renderer/bufferCache.cpp index 113a4f4a7..0cdfe790f 100644 --- a/src/graphics/host_gpu/renderer/bufferCache.cpp +++ b/src/graphics/host_gpu/renderer/bufferCache.cpp @@ -1001,7 +1001,7 @@ std::pair BufferCache::ObtainBuffer(CommandBuffer* com " size=0x%016" PRIx64 " read=%d written=%d formatted=%d\n", vaddr, size, is_read, is_written, is_formatted); } - m_texture_cache->InvalidateMemoryFromGPU(vaddr, size, true); + (void)m_texture_cache->InvalidateMemoryFromGPU(vaddr, size, true); } } if (is_written) { @@ -1421,6 +1421,7 @@ void BufferCache::CopyBuffer(CommandBuffer* command, GraphicContext* ctx, uint64 } ValidateGpuAccess(src_vaddr, size, true, false); ValidateGpuAccess(dst_vaddr, size, false, true); + bool dst_image_transition = false; { std::lock_guard transaction(m_resource_mutex); const bool src_image_gpu = m_texture_cache->HasGpuModifiedRangeOverlap(src_vaddr, size); @@ -1452,7 +1453,7 @@ void BufferCache::CopyBuffer(CommandBuffer* command, GraphicContext* ctx, uint64 " dst=0x%016" PRIx64 " size=0x%016" PRIx64 "\n", src_vaddr, dst_vaddr, size); } - m_texture_cache->InvalidateMemoryFromGPU(dst_vaddr, size); + dst_image_transition = m_texture_cache->InvalidateMemoryFromGPU(dst_vaddr, size); // A clean target destination is handled above like a protected host write. Target // aliases that require an actual GPU buffer copy remain unsupported. if (m_texture_cache->HasGpuTargetPageOverlap(dst_vaddr, size)) { @@ -1470,7 +1471,8 @@ void BufferCache::CopyBuffer(CommandBuffer* command, GraphicContext* ctx, uint64 } } const auto [src, src_offset] = ObtainBuffer(command, ctx, src_vaddr, size, false, true); - const auto [dst, dst_offset] = ObtainBuffer(command, ctx, dst_vaddr, size, true, false); + const auto [dst, dst_offset] = + ObtainBuffer(command, ctx, dst_vaddr, size, true, false, dst_image_transition); if (src == dst && src_offset < dst_offset + size && dst_offset < src_offset + size) { EXIT("BufferCache: resolved Vulkan copy ranges overlap, src_offset=0x%016" PRIx64 " dst_offset=0x%016" PRIx64 " size=0x%016" PRIx64 "\n", diff --git a/src/graphics/host_gpu/renderer/colorRenderTarget.cpp b/src/graphics/host_gpu/renderer/colorRenderTarget.cpp index c995f04c2..15ff9bd97 100644 --- a/src/graphics/host_gpu/renderer/colorRenderTarget.cpp +++ b/src/graphics/host_gpu/renderer/colorRenderTarget.cpp @@ -330,6 +330,7 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H rt.view.base_array_slice_index, rt.view.last_array_slice_index, render_target_slice_offset); } + r->base_array_layer = view.base_layer; const uint32_t levels = rt.attrib2.num_mip_levels + 1u; if (levels == 0 || levels > 16 || rt.view.current_mip_level >= levels) { EXIT("unsupported render-target mip range: current=%u levels=%u\n", diff --git a/src/graphics/host_gpu/renderer/imageInfo.h b/src/graphics/host_gpu/renderer/imageInfo.h index bccede86b..65a998881 100644 --- a/src/graphics/host_gpu/renderer/imageInfo.h +++ b/src/graphics/host_gpu/renderer/imageInfo.h @@ -624,7 +624,7 @@ ClassifyRenderTargetOverlap(const RenderTargetInfo& cached, bool cached_gpu_modi } // For an equal-address allocation-pool entry, a changed block raster or block size is a new // image allocation, not a view of the old image. In Kyty we can only retire an - // already-published target with page-isolated storage :) + // already-published target with page-isolated storage. const bool page_isolated = cached.address % TRACKER_PAGE_SIZE == 0 && cached.size % TRACKER_PAGE_SIZE == 0 && requested.address % TRACKER_PAGE_SIZE == 0 && requested.size % TRACKER_PAGE_SIZE == 0; diff --git a/src/graphics/host_gpu/renderer/renderDraw.cpp b/src/graphics/host_gpu/renderer/renderDraw.cpp index a6380979b..53bf5933c 100644 --- a/src/graphics/host_gpu/renderer/renderDraw.cpp +++ b/src/graphics/host_gpu/renderer/renderDraw.cpp @@ -32,6 +32,7 @@ #include "libs/errno.h" #include +#include #include #include #include @@ -1247,6 +1248,24 @@ void RenderDrawIndexAuto(uint64_t submit_id, CommandBuffer* buffer, HW::Context* index_bind, false, false, true); } +bool IsSameColorResolveSubresource(const RenderColorInfo& src, const RenderColorInfo& dst) { + return src.base_addr == dst.base_addr && src.base_mip_level == dst.base_mip_level && + src.base_array_layer == dst.base_array_layer; +} + +ImageImageCopy MakeColorResolveCopy(const RenderColorInfo& src, const RenderColorInfo& dst, + uint32_t width, uint32_t height) { + ImageImageCopy region {}; + region.src_image = src.vulkan_buffer; + region.src_level = src.base_mip_level; + region.dst_level = dst.base_mip_level; + region.width = width; + region.height = height; + region.src_layer = src.base_array_layer; + region.dst_layer = dst.base_array_layer; + return region; +} + static bool ResolveColorTargets(uint64_t submit_id, CommandBuffer* buffer, const HW::Context& hw, uint32_t render_target_slice_offset) { if (hw.GetColorControl().mode != 3) { @@ -1268,7 +1287,7 @@ static bool ResolveColorTargets(uint64_t submit_id, CommandBuffer* buffer, const src.type == RenderColorType::NoColorOutput || dst.type == RenderColorType::NoColorOutput) { return false; } - if (src.base_addr == dst.base_addr && src.base_mip_level == dst.base_mip_level) { + if (IsSameColorResolveSubresource(src, dst)) { return true; } @@ -1278,14 +1297,7 @@ static bool ResolveColorTargets(uint64_t submit_id, CommandBuffer* buffer, const return false; } - std::vector regions; - ImageImageCopy region {}; - region.src_image = src.vulkan_buffer; - region.src_level = src.base_mip_level; - region.dst_level = dst.base_mip_level; - region.width = width; - region.height = height; - regions.push_back(region); + const std::array regions {MakeColorResolveCopy(src, dst, width, height)}; MarkRenderTargetGpuWritten(dst); UtilImageToImage(buffer, regions, dst.vulkan_buffer, dst.vulkan_buffer->layout); diff --git a/src/graphics/host_gpu/renderer/renderState.h b/src/graphics/host_gpu/renderer/renderState.h index 5b9d45268..81279f041 100644 --- a/src/graphics/host_gpu/renderer/renderState.h +++ b/src/graphics/host_gpu/renderer/renderState.h @@ -15,6 +15,7 @@ namespace Libs::Graphics { class CommandBuffer; struct DepthStencilVulkanImage; +struct ImageImageCopy; struct RenderTextureVulkanImage; struct ShaderVertexInputInfo; struct VulkanImage; @@ -236,6 +237,7 @@ struct RenderColorInfo { VkFormat format = VK_FORMAT_UNDEFINED; VkExtent2D extent = {}; uint32_t base_mip_level = 0; + uint32_t base_array_layer = 0; uint64_t base_addr = 0; uint64_t buffer_size = 0; uint32_t target_slot = 0; @@ -243,6 +245,12 @@ struct RenderColorInfo { VkClearColorValue color_clear_value {}; }; +[[nodiscard]] bool IsSameColorResolveSubresource(const RenderColorInfo& src, + const RenderColorInfo& dst); +[[nodiscard]] ImageImageCopy MakeColorResolveCopy(const RenderColorInfo& src, + const RenderColorInfo& dst, uint32_t width, + uint32_t height); + struct ScissorRect { int left = 0; int top = 0; diff --git a/src/graphics/host_gpu/renderer/textureCache.cpp b/src/graphics/host_gpu/renderer/textureCache.cpp index b6ef60a52..9d9b9d89b 100644 --- a/src/graphics/host_gpu/renderer/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/textureCache.cpp @@ -387,18 +387,9 @@ struct TextureCache::ReadbackWorker { } download.resize(info.size); std::fill(download.begin(), download.end(), 0); - std::vector regions; - regions.reserve(info.layers); - for (uint32_t layer = 0; layer < info.layers; layer++) { - ImageBufferCopy region {}; - region.offset = static_cast(slice_size * layer); - region.pitch = info.pitch; - region.width = info.width; - region.height = info.height; - region.src_layer = layer; - region.aspect = VK_IMAGE_ASPECT_DEPTH_BIT; - regions.push_back(region); - } + const auto regions = + MakeLayeredImageBufferCopies(info.layers, slice_size, info.pitch, info.width, + info.height, VK_IMAGE_ASPECT_DEPTH_BIT); UtilFillBuffer(cached.ctx, download.data(), info.size, regions, cached.image, cached.image->layout); guest.resize(info.size); @@ -467,17 +458,8 @@ struct TextureCache::ReadbackWorker { } download.resize(info.size); std::fill(download.begin(), download.end(), 0); - std::vector regions; - regions.reserve(layers); - for (uint32_t layer = 0; layer < layers; layer++) { - ImageBufferCopy region {}; - region.offset = static_cast(slice_size * layer); - region.pitch = info.pitch; - region.width = info.width; - region.height = info.height; - region.src_layer = layer; - regions.push_back(region); - } + const auto regions = + MakeLayeredImageBufferCopies(layers, slice_size, info.pitch, info.width, info.height); UtilFillBuffer(cached.ctx, download.data(), info.size, regions, cached.image, cached.image->layout); if (tiled) { @@ -3046,7 +3028,6 @@ void TextureCache::MarkGpuWritten(VulkanImage* image) { cached->depth.htile_address, cached->depth.htile_size, true, [](uint64_t, uint64_t) noexcept {}, []() noexcept {}); meta->second.gpu_modified = true; - meta->second.clear_mask = 0; } } if (!cached->gpu_modified) { @@ -3110,29 +3091,34 @@ void TextureCache::SynchronizeRenderTargetToBufferLocked(CachedImage& cached) { target.tile_mode == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget); const auto rows = static_cast(target.height - 1); TileSizeAlign exact {}; - const bool exact_tiled = tiled && - TileGetRenderTargetSize(target.width, target.height, target.pitch, - target.bytes_per_element, &exact) && - exact.align == 65536 && exact.size == target.size; + const bool single_slice = TileGetRenderTargetSize(target.width, target.height, target.pitch, + target.bytes_per_element, &exact); + const bool layered_size = target.layers != 0 && single_slice && + exact.size <= UINT64_MAX / target.layers && + exact.size * target.layers == target.size; + const bool exact_tiled = tiled && exact.align == 65536 && layered_size; if (cached.kind != CachedImage::Kind::RenderTarget || !cached.gpu_modified || cached.buffer_modified || cached.ctx == nullptr || cached.image == nullptr || (!linear && !exact_tiled) || target.address == 0 || target.size == 0 || target.width == 0 || target.height == 0 || target.pitch < target.width || target.bytes_per_element == 0 || - target.levels != 1 || rows > (UINT64_MAX - target.width) / target.pitch) { + target.layers == 0 || target.size % target.layers != 0 || target.size > UINT32_MAX || + target.levels != 1 || cached.image->layers != target.layers || + rows > (UINT64_MAX - target.width) / target.pitch) { EXIT("TextureCache: unsupported render-target buffer synchronization, " "addr=0x%016" PRIx64 " size=0x%016" PRIx64 - " extent=%ux%u pitch=%u bpe=%u levels=%u tile=%u" + " extent=%ux%u pitch=%u bpe=%u levels=%u layers=%u/%u tile=%u" " gpu_modified=%d buffer_modified=%d\n", target.address, target.size, target.width, target.height, target.pitch, - target.bytes_per_element, target.levels, target.tile_mode, cached.gpu_modified, - cached.buffer_modified); + target.bytes_per_element, target.levels, target.layers, cached.image->layers, + target.tile_mode, cached.gpu_modified, cached.buffer_modified); } const auto linear_elements = rows * target.pitch + target.width; if (linear_elements > UINT64_MAX / target.bytes_per_element) { EXIT("TextureCache: render-target buffer synchronization size overflow\n"); } const auto linear_size = linear_elements * target.bytes_per_element; - if (linear_size > target.size || cached.image->format != target.format || + const auto slice_size = target.size / target.layers; + if (linear_size > slice_size || cached.image->format != target.format || cached.image->extent.width != target.width || cached.image->extent.height != target.height || (tiled && target.bytes_per_element != 1 && target.bytes_per_element != 2 && @@ -3149,7 +3135,9 @@ void TextureCache::SynchronizeRenderTargetToBufferLocked(CachedImage& cached) { VulkanDeviceWaitIdle(cached.ctx); m_buffer_transition_linear.resize(target.size); std::fill(m_buffer_transition_linear.begin(), m_buffer_transition_linear.end(), 0); - UtilFillBuffer(cached.ctx, m_buffer_transition_linear.data(), linear_size, target.pitch, + const auto regions = MakeLayeredImageBufferCopies(target.layers, slice_size, target.pitch, + target.width, target.height); + UtilFillBuffer(cached.ctx, m_buffer_transition_linear.data(), target.size, regions, cached.image, cached.image->layout); if (tiled) { m_buffer_transition_guest.resize(target.size); @@ -3171,7 +3159,7 @@ void TextureCache::SynchronizeRenderTargetToBufferLocked(CachedImage& cached) { cached.buffer_modified = true; } -void TextureCache::InvalidateMemoryFromGPU(uint64_t vaddr, uint64_t size, +bool TextureCache::InvalidateMemoryFromGPU(uint64_t vaddr, uint64_t size, bool formatted_buffer_write) { if (vaddr == 0 || size == 0 || vaddr >= TRACKER_ADDRESS_SIZE || size > TRACKER_ADDRESS_SIZE - vaddr) { @@ -3226,23 +3214,24 @@ void TextureCache::InvalidateMemoryFromGPU(uint64_t vaddr, uint64_t size, sampled_write = sampled; } if (match == m_images.end()) { - return; + return false; } if (sampled_write) { (**match).buffer_modified = true; - return; + return true; } if (action == BufferImageAlias::VideoOutWrite) { auto& cached = **match; cached.buffer_modified = true; - return; + return true; } if (action == BufferImageAlias::RenderTargetWrite) { SynchronizeRenderTargetToBufferLocked(**match); - return; + return true; } m_memory_tracker.UntrackMemory(vaddr, size); m_images.erase(match); + return true; } DepthStencilVulkanImage* TextureCache::FindDepthTargetByRange(uint64_t vaddr, uint64_t size) { diff --git a/src/graphics/host_gpu/renderer/textureCache.h b/src/graphics/host_gpu/renderer/textureCache.h index 39a18aaa3..8691a6736 100644 --- a/src/graphics/host_gpu/renderer/textureCache.h +++ b/src/graphics/host_gpu/renderer/textureCache.h @@ -56,7 +56,7 @@ public: uint64_t size, uint32_t packed_clear); void MarkGpuWritten(VulkanImage* image); void PrepareHostWrite(uint64_t vaddr, uint64_t size); - void InvalidateMemoryFromGPU(uint64_t vaddr, uint64_t size, + [[nodiscard]] bool InvalidateMemoryFromGPU(uint64_t vaddr, uint64_t size, bool formatted_buffer_write = false); [[nodiscard]] RenderTextureVulkanImage* FindRenderTargetByRange(CommandBuffer* command, uint64_t vaddr, uint64_t size); diff --git a/src/graphics/host_gpu/utils.cpp b/src/graphics/host_gpu/utils.cpp index 21caaf679..1a95ea632 100644 --- a/src/graphics/host_gpu/utils.cpp +++ b/src/graphics/host_gpu/utils.cpp @@ -22,6 +22,27 @@ static Common::Mutex g_scratch_buffer_mutex; static std::unique_ptr g_scratch_buffer; static uint64_t g_scratch_buffer_capacity = 0; +std::vector MakeLayeredImageBufferCopies(uint32_t layers, uint64_t slice_size, + uint32_t pitch, uint32_t width, + uint32_t height, + VkImageAspectFlags aspect) { + if (layers == 0 || slice_size == 0 || slice_size > UINT32_MAX || + slice_size > UINT32_MAX / layers || pitch < width || width == 0 || height == 0) { + EXIT("invalid layered image-buffer copy layout\n"); + } + std::vector regions(layers); + for (uint32_t layer = 0; layer < layers; layer++) { + auto& region = regions[layer]; + region.offset = static_cast(slice_size * layer); + region.pitch = pitch; + region.width = width; + region.height = height; + region.src_layer = layer; + region.aspect = aspect; + } + return regions; +} + UtilScratchBuffer::UtilScratchBuffer(uint64_t size) { EXIT_IF(size == 0); diff --git a/src/graphics/host_gpu/utils.h b/src/graphics/host_gpu/utils.h index a7f5e0500..53eee2c0d 100644 --- a/src/graphics/host_gpu/utils.h +++ b/src/graphics/host_gpu/utils.h @@ -6,6 +6,7 @@ #include #include +#include #include namespace Libs::Graphics { @@ -64,6 +65,11 @@ struct ImageImageCopy { int dst_z = 0; }; +[[nodiscard]] std::vector +MakeLayeredImageBufferCopies(uint32_t layers, uint64_t slice_size, uint32_t pitch, uint32_t width, + uint32_t height, + VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT); + enum class StagingBufferType { Texture, Vertex, ReadBack }; class UtilScratchBuffer { diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 0a3577477..34d324ecc 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -15,7 +15,9 @@ #include "graphics/host_gpu/renderer/render.h" #include "graphics/host_gpu/renderer/renderState.h" #include "graphics/host_gpu/renderer/resourceMutex.h" +#include "graphics/host_gpu/renderer/tiler.h" #include "graphics/host_gpu/renderer/textureCache.h" +#include "graphics/host_gpu/utils.h" #include "graphics/shader/recompiler/BindingLayout.h" #include "graphics/shader/recompiler/ShaderDecoder.h" #include "graphics/shader/recompiler/ShaderRecompiler.h" @@ -9485,6 +9487,31 @@ void CheckStorageTextureVolumeUploadLayout() { std::printf("[host] %-32s ok\n", "StorageTextureVolumeUpload"); } +void CheckColorResolveLayers() { + RenderColorInfo src{}; + RenderColorInfo dst{}; + src.base_addr = dst.base_addr = 0x12340000; + src.base_mip_level = dst.base_mip_level = 2; + src.base_array_layer = 1; + dst.base_array_layer = 3; + src.vulkan_buffer = reinterpret_cast(uintptr_t{1}); + + Require("ColorResolveLayers", "identity", + !IsSameColorResolveSubresource(src, dst), + "different array layers were treated as the same resolve subresource"); + const auto copy = MakeColorResolveCopy(src, dst, 128, 64); + Require("ColorResolveLayers", "region", + copy.src_image == src.vulkan_buffer && copy.src_level == 2 && + copy.dst_level == 2 && copy.src_layer == 1 && + copy.dst_layer == 3 && copy.width == 128 && copy.height == 64, + "color resolve dropped its source or destination array layer"); + dst.base_array_layer = src.base_array_layer; + Require("ColorResolveLayers", "same subresource", + IsSameColorResolveSubresource(src, dst), + "matching color resolve subresources were not recognized"); + std::printf("[host] %-32s ok\n", "ColorResolveLayers"); +} + void CheckRenderTargetTileRoundTrip() { struct Case { uint32_t width; @@ -9529,6 +9556,64 @@ void CheckRenderTargetTileRoundTrip() { "linear-to-tiled render-target conversion did not round-trip"); } } + constexpr uint32_t width = 257; + constexpr uint32_t height = 131; + constexpr uint32_t bytes_per_element = 4; + constexpr uint32_t layers = 3; + const auto pitch = TileGetRenderTargetPitch(width, bytes_per_element); + TileSizeAlign storage{}; + Require("RenderTargetTileRoundTrip", "layered layout", + TileGetRenderTargetSize(width, height, pitch, bytes_per_element, + &storage) && + storage.size <= UINT32_MAX / layers, + "layered render-target test layout was rejected"); + RenderTargetInfo info{}; + info.address = 1; + info.size = storage.size * layers; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.width = width; + info.height = height; + info.pitch = pitch; + info.bytes_per_element = bytes_per_element; + info.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget); + info.layers = layers; + std::vector linear(info.size, 0); + for (uint32_t layer = 0; layer < layers; layer++) { + for (uint32_t y = 0; y < height; y++) { + const auto row = storage.size * layer + + static_cast(y) * pitch * bytes_per_element; + std::fill_n(linear.data() + row, width * bytes_per_element, + static_cast(0x31 + layer * 0x27)); + } + } + std::vector tiled(info.size, 0xcd); + std::vector restored(info.size, 0xa5); + Tiler tiler; + tiler.TileImage(tiled.data(), linear.data(), info); + for (uint32_t layer = 0; layer < layers; layer++) { + TileConvertTiledToLinearRenderTarget( + restored.data() + storage.size * layer, + tiled.data() + storage.size * layer, width, height, pitch, + bytes_per_element, storage.size); + for (uint32_t y = 0; y < height; y++) { + const auto row = storage.size * layer + + static_cast(y) * pitch * bytes_per_element; + Require("RenderTargetTileRoundTrip", "layered contents", + std::memcmp(linear.data() + row, restored.data() + row, + width * bytes_per_element) == 0, + "layered render-target conversion crossed array slices"); + } + } + const auto regions = MakeLayeredImageBufferCopies( + layers, storage.size, pitch, width, height); + Require("RenderTargetTileRoundTrip", "layered regions", + regions.size() == layers && regions[0].offset == 0 && + regions[1].offset == storage.size && + regions[2].offset == storage.size * 2 && + regions[0].src_layer == 0 && regions[1].src_layer == 1 && + regions[2].src_layer == 2 && regions[2].pitch == pitch && + regions[2].width == width && regions[2].height == height, + "layered image-buffer regions did not preserve slice offsets"); std::printf("[host] %-32s ok\n", "RenderTargetTileRoundTrip"); } @@ -10083,7 +10168,8 @@ void CheckOverlappingMetadataViews() { void CheckGpuMetadataReuse() { constexpr uintptr_t base = 0x0000000200010000ull; constexpr uint64_t allocation_size = 0x20000; - constexpr uint64_t metadata_size = 0x8000; + constexpr uint64_t metadata_size = 0x18000; + constexpr uint32_t layers = 3; auto *memory = static_cast(VirtualAlloc( reinterpret_cast(base), allocation_size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE)); @@ -10100,22 +10186,34 @@ void CheckGpuMetadataReuse() { buffer_cache.SetTextureCache(texture_cache); page_manager.OnGpuMap(base, allocation_size); - texture_cache.RegisterMeta(base, metadata_size); + texture_cache.RegisterMeta(base, metadata_size, layers); Require("GpuMetadataReuse", "clear", texture_cache.ClearMeta(base), "metadata clear setup failed"); - texture_cache.InvalidateMemoryFromGPU(base, allocation_size); + const bool full_image_transition = + texture_cache.InvalidateMemoryFromGPU(base, allocation_size); Require("GpuMetadataReuse", "discard", - !texture_cache.IsMeta(base) && + !full_image_transition && !texture_cache.IsMeta(base) && !texture_cache.HasMetaRangeOverlap(base, allocation_size), - "fully overwritten metadata identity was retained"); - texture_cache.RegisterMeta(base, metadata_size); + "metadata-only overwrite retained identity or claimed an image transition"); + texture_cache.RegisterMeta(base, metadata_size, layers); Require("GpuMetadataReuse", "re-register", texture_cache.IsMetaRange(base, metadata_size) && texture_cache.ClearMeta(base), "metadata identity could not be reused after a GPU overwrite"); - texture_cache.InvalidateMemoryFromGPU(base + 0x1000, 0x1000); - Require("GpuMetadataReuse", "partial discard", !texture_cache.IsMeta(base), - "partially overwritten metadata identity was retained"); + Require("GpuMetadataReuse", "layered clear", + texture_cache.IsMetaCleared(base, 0) && + texture_cache.IsMetaCleared(base, 1) && + texture_cache.IsMetaCleared(base, 2) && + texture_cache.TouchMeta(base, 1, false) && + texture_cache.IsMetaCleared(base, 0) && + !texture_cache.IsMetaCleared(base, 1) && + texture_cache.IsMetaCleared(base, 2), + "consuming one metadata slice erased another slice's lazy clear"); + const bool partial_image_transition = + texture_cache.InvalidateMemoryFromGPU(base + 0x1000, 0x1000); + Require("GpuMetadataReuse", "partial discard", + !partial_image_transition && !texture_cache.IsMeta(base), + "partial metadata overwrite retained identity or claimed an image transition"); texture_cache.UnmapMemory(base, allocation_size); page_manager.OnGpuUnmap(base, allocation_size); @@ -11379,6 +11477,13 @@ int main(int argc, char **argv) { CheckOverlappingMetadataViews(); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--layered-image-only") == 0) { + CheckColorResolveLayers(); + CheckRenderTargetTileRoundTrip(); + CheckDepthTargetTileRoundTrip(); + CheckGpuMetadataReuse(); + return 0; + } if (argc == 2 && std::strcmp(argv[1], "--image-view-only") == 0) { CheckSampledColorViews(); return 0; @@ -11445,6 +11550,7 @@ int main(int argc, char **argv) { CheckStorageTextureDepthTileUploadLayout(); CheckStorageTextureLinearReadbackLayout(); CheckStorageImageSwizzleSpecializationId(); + CheckColorResolveLayers(); CheckRenderTargetTileRoundTrip(); CheckDepthTargetTileRoundTrip(); CheckStencilTargetTileRoundTrip();