From 8a6afc428d85893f3fdaf26ab508ec5f509d907f Mon Sep 17 00:00:00 2001 From: nmzik Date: Fri, 17 Jul 2026 22:37:02 +0200 Subject: [PATCH] support storage descriptors and tracked image aliases --- .../host_gpu/renderer/descriptors.cpp | 209 +++++++++--- .../host_gpu/renderer/renderCompute.cpp | 14 +- .../recompiler/ResourceMaterialization.cpp | 9 + .../shader/recompiler/ResourceTracking.cpp | 19 ++ src/graphics/shader/recompiler/ShaderIR.h | 3 + src/graphics/shader/shaderBindings.h | 1 + tests/ResourceTrackingTests.cpp | 16 + tests/ShaderRecompilerComputeTests.cpp | 316 +++++++++++++++++- 8 files changed, 530 insertions(+), 57 deletions(-) diff --git a/src/graphics/host_gpu/renderer/descriptors.cpp b/src/graphics/host_gpu/renderer/descriptors.cpp index cc5bfeefb..221cc5de8 100644 --- a/src/graphics/host_gpu/renderer/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/descriptors.cpp @@ -156,9 +156,60 @@ NativeSamplerDescriptor(const ShaderRecompiler::IR::DescriptorValue& descriptor) } static BufferView NativeStorageBuffer(uint64_t submit_id, CommandBuffer* command_buffer, - const ShaderBufferResource& descriptor, - const ShaderRecompiler::IR::BufferResource& resource) { + const ShaderBufferResource& descriptor, + const ShaderRecompiler::IR::BufferResource& resource) { BufferView result; + // Bind a null buffer when these four dwords are + // the tracked prefix of an active image sharp. Image validity is encoded by dword 3 bit 31. + if (resource.image_alias != ShaderRecompiler::IR::BufferResource::NoImageAlias && + (descriptor.Type() & 2u) != 0) { + BindNullStorageBuffer(command_buffer, &result); + return result; + } + // Buffer TYPE is zero. A nonzero value means a buffer instruction received the first four + // dwords of an image sharp without a tracked image alias; support the known write-only path. + if (descriptor.Type() != 0) { + ShaderTextureResource texture {}; + std::copy_n(descriptor.fields, 4, texture.fields); + const auto width = static_cast(texture.Width5()) + 1u; + const auto height = static_cast(texture.Height5()) + 1u; + const auto format = texture.Format(); + const auto tile = texture.TileMode(); + const auto type = texture.Type(); + const auto address = texture.Base40(); + const auto pitch = TileGetTexturePitch(format, width, 1, tile); + TileSizeAlign footprint {}; + TileGetTextureTotalSize(format, width, height, 1, pitch, 1, tile, false, &footprint); + const bool supported = + resource.formatted && resource.written && !resource.read && !resource.atomic && + format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UInt) && + tile == Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) && + type == Prospero::GpuEnumValue(Prospero::ImageType::kColor2D) && + texture.DstSelXYZW() == DstSel(4, 5, 6, 7) && address != 0 && footprint.size != 0 && + footprint.align == 65536; + if (!supported) { + EXIT("unsupported texture descriptor used as storage buffer: type=%u format=%u" + " tile=%u swizzle=0x%03x extent=%ux%u read=%d written=%d formatted=%d atomic=%d\n", + type, format, tile, texture.DstSelXYZW(), width, height, resource.read, + resource.written, resource.formatted, resource.atomic); + } + auto* ctx = g_render_ctx->GetGraphicCtx(); + g_render_ctx->GetBufferCache()->ValidateGpuAccess(address, footprint.size, false, true); + auto binding = g_render_ctx->GetBufferCache()->ObtainBuffer( + command_buffer, ctx, address, footprint.size, true, false, true); + const auto alignment = ctx->StorageMinAlignment(); + if (alignment == 0 || binding.second % alignment != 0 || + footprint.size > ctx->GetPhysicalDeviceProperties().limits.maxStorageBufferRange) { + EXIT("texture-backed storage buffer binding is unsupported: addr=0x%016" PRIx64 + " size=0x%016" PRIx64 " offset=0x%016" PRIx64 " alignment=0x%016" PRIx64 "\n", + address, footprint.size, static_cast(binding.second), + static_cast(alignment)); + } + result.buffer = binding.first; + result.offset = binding.second; + result.range = footprint.size; + return result; + } const auto address = descriptor.Base48(); const auto stride = descriptor.Stride(); const auto records = descriptor.NumRecords(); @@ -247,7 +298,9 @@ static bool IsSupportedSampledColorResource(const ShaderRecompiler::IR::ImageRes break; default: break; } - return resource.kind == ShaderRecompiler::IR::ResourceKind::Image && supported_dimension && + const bool sampled_kind = resource.kind == ShaderRecompiler::IR::ResourceKind::Image || + resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint; + return sampled_kind && supported_dimension && resource.mip_mode == ShaderRecompiler::IR::ImageMipMode::None && resource.read && !resource.written && !resource.atomic && !resource.depth_compare; } @@ -268,6 +321,10 @@ ResolveTargetTextureView(const ShaderRecompiler::IR::ImageResource& resource, ? TargetTextureViewInfo {VK_IMAGE_VIEW_TYPE_2D, 0, 1} : TargetTextureViewInfo {}; case Prospero::ImageType::kColor2DArray: + if (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D && + base_layer == 0 && image_layers == 1) { + return {VK_IMAGE_VIEW_TYPE_2D, 0, 1}; + } return resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray && base_layer < image_layers ? TargetTextureViewInfo {VK_IMAGE_VIEW_TYPE_2D_ARRAY, base_layer, @@ -321,20 +378,21 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource TileGetTexturePitch(descriptor.Format(), static_cast(descriptor.Width5()) + 1u, 1, descriptor.TileMode()); EXIT("unsupported sampled depth target: resource=%d descriptor=%d encoding=%d format=%d " - "kind=%u dimension=%u mip=%u read=%d written=%d atomic=%d compare=%d " - "image_format=%d view_format=%d image_layers=%u descriptor_type=%u base_array=%u " - "depth=%u descriptor_pitch=%u target_pitch=%u fields=%08x/%08x/%08x/%08x/%08x/" - "%08x/%08x/%08x addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n", - resource_ok, descriptor_ok, encoding_ok, format_ok, - static_cast(resource.kind), static_cast(resource.dimension), - static_cast(resource.mip_mode), resource.read, resource.written, resource.atomic, - resource.depth_compare, + "kind=%u dimension=%u mip_mode=%u read=%d written=%d atomic=%d compare=%d " + "guest_format=%u swizzle=0x%03x image_format=%d view_format=%d image_layers=%u " + "descriptor_type=%u base_array=%u depth=%u descriptor_pitch=%u target_pitch=%u " + "addr=0x%016" PRIx64 " size=0x%016" PRIx64 + " dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n", + resource_ok, descriptor_ok, encoding_ok, format_ok, static_cast(resource.kind), + static_cast(resource.dimension), static_cast(resource.mip_mode), + resource.read, resource.written, resource.atomic, resource.depth_compare, + descriptor.Format(), descriptor.DstSelXYZW(), image == nullptr ? static_cast(VK_FORMAT_UNDEFINED) : static_cast(image->format), static_cast(view_format), image == nullptr ? 0u : image->layers, descriptor.Type(), descriptor.BaseArray5(), descriptor.Depth(), descriptor_pitch, - image == nullptr ? 0u : image->guest_pitch, descriptor.fields[0], descriptor.fields[1], - descriptor.fields[2], descriptor.fields[3], descriptor.fields[4], descriptor.fields[5], - descriptor.fields[6], descriptor.fields[7], descriptor.Base40(), size); + image == nullptr ? 0u : image->guest_pitch, descriptor.Base40(), size, + descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3], + descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]); } static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::ImageResource& resource, @@ -362,8 +420,10 @@ static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::Imag const bool supported_swizzle = IsSupportedStorageSwizzle(descriptor.Format(), descriptor.DstSelXYZW()) && (descriptor.DstSelXYZW() == DstSel(4, 5, 6, 7) || !resource.read); - return (is_2d || is_2d_array || is_3d) && supported_tile && descriptor.BaseLevel() == 0 && - descriptor.LastLevel() == 0 && descriptor.MaxMip() == 0 && descriptor.MinLod() == 0 && + const bool supported_mip_view = descriptor.BaseLevel() == 0 || is_2d; + return (is_2d || is_2d_array || is_3d) && supported_tile && supported_mip_view && + descriptor.BaseLevel() == descriptor.LastLevel() && + descriptor.LastLevel() <= descriptor.MaxMip() && descriptor.MinLod() == 0 && descriptor.BaseArray5() == 0 && supported_swizzle && descriptor.BCSwizzle() == 0 && !descriptor.MsaaDepth(); } @@ -372,14 +432,17 @@ static bool IsSupportedStorageTextureEncoding(const ShaderTextureResource& descr constexpr uint32_t field1_reserved_mask = 0x200fff00u; constexpr uint32_t field2_reserved_mask = 0xf0003000u; constexpr uint32_t field5_expected = 0x00700000u; + constexpr uint32_t field5_max_mip_mask = 0x000000f0u; const uint32_t expected_field3 = descriptor.DstSelXYZW() | + (static_cast(descriptor.BaseLevel()) << 12u) | + (static_cast(descriptor.LastLevel()) << 16u) | (static_cast(descriptor.TileMode()) << 20u) | (static_cast(descriptor.Type()) << 28u); return (descriptor.fields[1] & field1_reserved_mask) == 0 && (descriptor.fields[2] & field2_reserved_mask) == 0 && descriptor.fields[3] == expected_field3 && descriptor.fields[4] == descriptor.Depth() && - descriptor.fields[5] == field5_expected && descriptor.fields[6] == 0 && - descriptor.fields[7] == 0; + (descriptor.fields[5] & ~field5_max_mip_mask) == field5_expected && + descriptor.fields[6] == 0 && descriptor.fields[7] == 0; } void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource, @@ -396,13 +459,28 @@ void ValidateStorageTexture(const ShaderRecompiler::IR::ImageResource& resource, return; } EXIT("unsupported storage texture: resource=%d descriptor=%d encoding=%d format=%d " + "kind=%u dimension=%u mip_mode=%u atomic=%d compare=%d " + "base_level=%u last_level=%u max_mip=%u min_lod=%u base_array=%u bc=%u msaa=%d " + "depth_tile_shape=%d swizzle_ok=%d " "addr=0x%016" PRIx64 " size=0x%016" PRIx64 - " extent=%ux%ux%u type=%u format=%u tile=%u swizzle=0x%03x read=%d written=%d\n", - resource_ok, descriptor_ok, encoding_ok, format_ok, descriptor.Base40(), size, - static_cast(descriptor.Width5()) + 1u, + " extent=%ux%ux%u type=%u format=%u tile=%u swizzle=0x%03x read=%d written=%d " + "dwords=%08x,%08x,%08x,%08x,%08x,%08x,%08x,%08x\n", + resource_ok, descriptor_ok, encoding_ok, format_ok, static_cast(resource.kind), + static_cast(resource.dimension), static_cast(resource.mip_mode), + resource.atomic, resource.depth_compare, descriptor.BaseLevel(), descriptor.LastLevel(), + descriptor.MaxMip(), descriptor.MinLod(), descriptor.BaseArray5(), descriptor.BCSwizzle(), + descriptor.MsaaDepth(), + IsSupportedStorageDepthTile(descriptor.Format(), descriptor.Type(), + static_cast(descriptor.Width5()) + 1u, + static_cast(descriptor.Height5()) + 1u, + static_cast(descriptor.Depth()) + 1u), + IsSupportedStorageSwizzle(descriptor.Format(), descriptor.DstSelXYZW()), + descriptor.Base40(), size, static_cast(descriptor.Width5()) + 1u, static_cast(descriptor.Height5()) + 1u, static_cast(descriptor.Depth()) + 1u, descriptor.Type(), format, - descriptor.TileMode(), descriptor.DstSelXYZW(), resource.read, resource.written); + descriptor.TileMode(), descriptor.DstSelXYZW(), resource.read, resource.written, + descriptor.fields[0], descriptor.fields[1], descriptor.fields[2], descriptor.fields[3], + descriptor.fields[4], descriptor.fields[5], descriptor.fields[6], descriptor.fields[7]); } void ValidateMetadataReuseTexture(const ShaderRecompiler::IR::ImageResource& resource, @@ -451,6 +529,16 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer, const auto depth = static_cast(descriptor.Depth()) + 1u; const auto tile = descriptor.TileMode(); const auto format = descriptor.Format(); + const bool sampled_numeric_class = + storage || ((resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) == + Prospero::IsUintTextureFormat(format)); + if (!storage && + (resource.kind == ShaderRecompiler::IR::ResourceKind::Image || + resource.kind == ShaderRecompiler::IR::ResourceKind::ImageUint) && + !sampled_numeric_class) { + EXIT("sampled image numeric class mismatch: kind=%u format=%u addr=0x%016" PRIx64 "\n", + static_cast(resource.kind), format, address); + } const auto view_format = TextureGetFormat(format, storage ? TextureFormatUsage::Storage : TextureFormatUsage::Sampled); const auto type = static_cast(descriptor.Type()); @@ -476,37 +564,48 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer, descriptor.MsaaDepth(); if (image == nullptr) { if (check_depth) { - image = g_render_ctx->GetTextureCache()->FindDepthTargetByRange(address, size.size); + image = + g_render_ctx->GetTextureCache()->FindDepthTargetByRange(address, size.size, true); } else { image = g_render_ctx->GetTextureCache()->FindRenderTargetByRange(command_buffer, address, size.size); } if (image != nullptr) { if (check_depth) { - ValidateDepthTargetBinding(resource, descriptor, image, view_format, size.size); - view = SelectSampledDepthView(image->format, view_format, swizzle); - if (graphics_debug_dump_enabled()) { - static std::atomic_uint log_count {0}; - if (log_count.fetch_add(1, std::memory_order_relaxed) < 64) { - LOGF("SampledDepthTarget: bind addr=0x%016" PRIx64 " size=0x%016" PRIx64 - " format=%d view=%d\n", - address, size.size, static_cast(image->format), view); - } + const bool uint_reinterpret = + IsSupportedSampledDepthUintResource(resource) && + IsSupportedDepthTargetDescriptor(descriptor, *image) && + IsSupportedDepthTextureEncoding(descriptor) && + IsDepthUintTextureReinterpretation(image->format, descriptor.Format(), + view_format); + const bool uint_storage_reinterpret = + storage && IsSupportedStorageImageResource(resource) && + IsSupportedStorageTextureDescriptor(resource, descriptor) && + IsSupportedStorageTextureEncoding(descriptor) && + IsDepthUintTextureReinterpretation(image->format, descriptor.Format(), + view_format); + if (uint_reinterpret || uint_storage_reinterpret) { + image = nullptr; + } else { + ValidateDepthTargetBinding(resource, descriptor, image, view_format, size.size); + view = SelectSampledDepthView(image->format, view_format, swizzle); } } else { if (!(storage ? IsSupportedStorageImageResource(resource) : IsSupportedSampledColorResource(resource)) || image->type != VulkanImageType::RenderTexture || width != image->extent.width || height != image->extent.height || - (storage ? levels != 1 || base_level != 0 + (storage ? levels != image->mip_levels || base_level != 0 : levels != image->mip_levels || base_level >= levels) || target_view.type == VK_IMAGE_VIEW_TYPE_MAX_ENUM || target_view.base_layer >= image->layers || target_view.layer_count > image->layers - target_view.base_layer) { - EXIT("unsupported cached render-target image view: storage=%d resource=%u" + EXIT("unsupported cached render-target image view: storage=%d resource=%u " + "dimension=%u" " image_type=%u layers=%u extent=%ux%u/%ux%u depth=%u" " levels=%u/%u base_level=%u base_array=%u descriptor_type=%u\n", storage, static_cast(resource.kind), + static_cast(resource.dimension), static_cast(image->type), image->layers, width, height, image->extent.width, image->extent.height, depth, levels, image->mip_levels, base_level, descriptor.BaseArray5(), @@ -528,10 +627,10 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer, target_view.layer_count); } } - if (image_view == nullptr && image->image_view[view] == nullptr) { + if (image != nullptr && image_view == nullptr && image->image_view[view] == nullptr) { EXIT("required cached texture image view is missing\n"); } - if (storage) { + if (storage && image != nullptr) { g_render_ctx->GetTextureCache()->MarkGpuWritten(image); } } @@ -540,11 +639,32 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer, const auto video = Presentation::DisplayBufferFind(address); if (video.image != nullptr) { if (storage) { - EXIT("storage access to a video-out surface is unsupported\n"); - } - image = video.image; - if (!storage && swizzle == DstSel(6, 5, 4, 7)) { - view = VulkanImage::VIEW_BGRA; + const bool exact = + resource.kind == ShaderRecompiler::IR::ResourceKind::StorageImageUint && + resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2D && + !resource.read && resource.written && !resource.atomic && + format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8UInt) && + view_format == VK_FORMAT_R8G8B8A8_UINT && swizzle == DstSel(6, 5, 4, 7) && + width == video.image->extent.width && height == video.image->extent.height && + depth == 1 && levels == 1 && base_level == 0 && view_levels == 1 && + type == Prospero::ImageType::kColor2D && video.size == size.size && + video.pitch == pitch && + (video.image->format == VK_FORMAT_R8G8B8A8_SRGB || + video.image->format == VK_FORMAT_B8G8R8A8_SRGB) && + video.image->image_view[VulkanImage::VIEW_STORAGE] != nullptr; + if (!exact) { + EXIT("unsupported storage access to video-out surface: format=%u view=%d" + " extent=%ux%u size=0x%016" PRIx64 " pitch=%u\n", + format, static_cast(view_format), width, height, size.size, pitch); + } + image = video.image; + view = VulkanImage::VIEW_STORAGE; + g_render_ctx->GetTextureCache()->MarkGpuWritten(image); + } else { + image = video.image; + if (swizzle == DstSel(6, 5, 4, 7)) { + view = VulkanImage::VIEW_BGRA; + } } } } @@ -578,6 +698,9 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer, image = texture_cache->FindStorageTexture(command_buffer, g_render_ctx->GetGraphicCtx(), info); view = VulkanImage::VIEW_DEFAULT; + image_view = texture_cache->GetStorageTextureStorageView( + g_render_ctx->GetGraphicCtx(), static_cast(image), + base_level); } else { image = texture_cache->FindTexture(command_buffer, g_render_ctx->GetGraphicCtx(), info, metadata_read); @@ -589,7 +712,9 @@ NativeTexture(uint64_t submit_id, CommandBuffer* command_buffer, } } EXIT_NOT_IMPLEMENTED(image == nullptr); - if (resource.dimension == ShaderRecompiler::Decoder::ImageDimension::Dim2DArray) { + if (NeedsStaticSampledArrayView(resource.dimension == + ShaderRecompiler::Decoder::ImageDimension::Dim2DArray, + image_view != nullptr)) { view = SelectSampledTextureArrayView(image, view); } return {image, view, image_view}; diff --git a/src/graphics/host_gpu/renderer/renderCompute.cpp b/src/graphics/host_gpu/renderer/renderCompute.cpp index 50c68f38c..be2355a90 100644 --- a/src/graphics/host_gpu/renderer/renderCompute.cpp +++ b/src/graphics/host_gpu/renderer/renderCompute.cpp @@ -111,7 +111,7 @@ bool ResolveHtileClearTarget(const HW::DepthRenderTarget& z, uint64_t descriptor } } - const bool size_xy_valid = z.size.valid && (z.size.x_max != 0 || z.size.y_max != 0); + const bool size_xy_valid = z.size.valid; const bool wh_valid = z.width_height_valid && z.width != 0 && z.height != 0; if (!size_xy_valid && !wh_valid) { // Prospero emits an exact full-surface metadata descriptor even @@ -281,18 +281,6 @@ static bool TryConsumeComputeMetaClear(const ShaderComputeInputInfo& input, cons if (!cache->ClearMeta(target.address)) { EXIT("failed to record HTile compute clear\n"); } - const bool descriptor_backed = - current_references != 0 && !z.size.valid && !z.width_height_valid && !z.pitch_height_valid; - const uint32_t source_bit = current_references == 0 ? 1u : descriptor_backed ? 2u : 4u; - static std::atomic logged_sources {0}; - if ((logged_sources.fetch_or(source_bit, std::memory_order_relaxed) & source_bit) == 0) { - LOGF("HTileClear: native virtual clear source=%s shader=0x%016" PRIx64 " addr=0x%016" PRIx64 - " size=0x%016" PRIx64 "\n", - current_references == 0 ? "registered" - : descriptor_backed ? "descriptor" - : "derived", - program.shader_hash, target.address, target.size); - } return true; } diff --git a/src/graphics/shader/recompiler/ResourceMaterialization.cpp b/src/graphics/shader/recompiler/ResourceMaterialization.cpp index ec5c314e2..2aeb0e4e4 100644 --- a/src/graphics/shader/recompiler/ResourceMaterialization.cpp +++ b/src/graphics/shader/recompiler/ResourceMaterialization.cpp @@ -91,6 +91,15 @@ bool ValidateResourceSnapshot(const Program& program, const ResourceSnapshot& sn } } } + for (uint32_t i = 0; i < program.info.buffers.size(); i++) { + const auto alias = program.info.buffers[i].image_alias; + if (alias != BufferResource::NoImageAlias && alias >= program.info.images.size()) { + if (error != nullptr) { + *error = fmt::format("buffer resource {} has invalid image alias {}", i, alias); + } + return false; + } + } const auto CheckWidth = [&](const auto& values, uint32_t width, const char* kind) { for (uint32_t i = 0; i < values.size(); i++) { if (values[i].dword_count != width) { diff --git a/src/graphics/shader/recompiler/ResourceTracking.cpp b/src/graphics/shader/recompiler/ResourceTracking.cpp index 57d8b180b..232d15c5e 100644 --- a/src/graphics/shader/recompiler/ResourceTracking.cpp +++ b/src/graphics/shader/recompiler/ResourceTracking.cpp @@ -178,6 +178,7 @@ public: } } } + LinkImageAliases(); m_program.info = std::move(m_info); for (const auto& patch: m_patches) { patch.inst->memory.resource = patch.resource; @@ -290,6 +291,24 @@ private: resource->scalar = resource->scalar || inst.memory.kind == ResourceKind::ScalarBuffer; } + void LinkImageAliases() { + for (auto& buffer: m_info.buffers) { + const auto* buffer_source = GetDescriptorSource(m_program, buffer.source); + if (buffer_source == nullptr || buffer_source->dword_count != 4) { + continue; + } + for (uint32_t i = 0; i < m_info.images.size(); i++) { + const auto* image_source = GetDescriptorSource(m_program, m_info.images[i].source); + if (image_source != nullptr && image_source->dword_count == 8 && + std::equal(buffer_source->dwords.begin(), buffer_source->dwords.begin() + 4, + image_source->dwords.begin())) { + buffer.image_alias = i; + break; + } + } + } + } + uint32_t AddImage(const Instruction& inst) { for (uint32_t i = 0; i < m_info.images.size(); i++) { auto& image = m_info.images[i]; diff --git a/src/graphics/shader/recompiler/ShaderIR.h b/src/graphics/shader/recompiler/ShaderIR.h index e44415ea3..49b0007da 100644 --- a/src/graphics/shader/recompiler/ShaderIR.h +++ b/src/graphics/shader/recompiler/ShaderIR.h @@ -575,11 +575,14 @@ struct SrtPlan { }; struct BufferResource { + static constexpr uint32_t NoImageAlias = UINT32_MAX; + uint32_t source = 0; uint32_t first_use_pc = 0; uint32_t max_byte_extent = 0; uint32_t packed_stride = 0; uint32_t descriptor_format = 0; + uint32_t image_alias = NoImageAlias; bool read = false; bool written = false; bool atomic = false; diff --git a/src/graphics/shader/shaderBindings.h b/src/graphics/shader/shaderBindings.h index dad1f77d3..dbb63a4da 100644 --- a/src/graphics/shader/shaderBindings.h +++ b/src/graphics/shader/shaderBindings.h @@ -64,6 +64,7 @@ struct ShaderBufferResource { } [[nodiscard]] uint8_t Format() const { return (fields[3] >> 12u) & 0x7Fu; } [[nodiscard]] uint8_t OutOfBounds() const { return (fields[3] >> 28u) & 0x3u; } + [[nodiscard]] uint8_t Type() const { return (fields[3] >> 30u) & 0x3u; } }; struct ShaderTextureResource { diff --git a/tests/ResourceTrackingTests.cpp b/tests/ResourceTrackingTests.cpp index 4367aa003..24dbcaf4a 100644 --- a/tests/ResourceTrackingTests.cpp +++ b/tests/ResourceTrackingTests.cpp @@ -227,6 +227,21 @@ void TestScalarAndVectorBufferAlias() { "scalar/vector alias did not share one dense index"); } +void TestBufferImageAliasIsLinkedDuringTracking() { + Program program; + program.blocks.resize(1); + program.blocks[0].instructions = { + BufferUse(4, 0), BufferUse(8, 8), + ImageUse(12, Opcode::ImageLoad, ResourceKind::Image, + Decoder::ImageDimension::Dim2D)}; + + Prepare(&program); + Check(program.info.buffers.size() == 2 && program.info.images.size() == 1 && + program.info.buffers[0].image_alias == 0 && + program.info.buffers[1].image_alias == BufferResource::NoImageAlias, + "buffer/image descriptor provenance aliases were not linked during tracking"); +} + void TestImagesAndSamplers() { Program program; program.blocks.resize(1); @@ -1793,6 +1808,7 @@ int main() { try { RUN(TestDenseBufferPatching); RUN(TestScalarAndVectorBufferAlias); + RUN(TestBufferImageAliasIsLinkedDuringTracking); RUN(TestImagesAndSamplers); RUN(TestDynamicPhiResource); RUN(TestTrackingRequiresCompletedSrtPlan); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 78f9b228c..87d287901 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -8858,6 +8858,17 @@ void CheckSampledColorViews() { VK_FORMAT_R16G16B16A16_SFLOAT, DstSel(7, 6, 5, 4)) == VulkanImage::VIEW_ABGR, "reverse RGBA16F target did not select its reciprocal ABGR sampled view"); + Require("SampledColorViews", "mutable integer-class views", + SelectSampledColorView(VK_FORMAT_R16G16B16A16_SFLOAT, + VK_FORMAT_R16G16B16A16_UINT, + DstSel(4, 5, 6, 7)) == VulkanImage::VIEW_DEFAULT && + SelectSampledColorView(VK_FORMAT_R8G8B8A8_UNORM, + VK_FORMAT_R8G8B8A8_UINT, + DstSel(4, 5, 6, 7)) == VulkanImage::VIEW_DEFAULT && + SelectSampledColorView(VK_FORMAT_R8G8B8A8_UINT, + VK_FORMAT_R8G8B8A8_UNORM, + DstSel(4, 5, 6, 7)) == VulkanImage::VIEW_DEFAULT, + "compatible integer render-target sampled view was rejected"); Require("SampledColorViews", "D32 depth target", SelectSampledDepthView(VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_R32_SFLOAT, @@ -8905,6 +8916,10 @@ void CheckSampledColorViews() { SelectStorageColorView(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, DstSel(6, 5, 4, 7)) == VulkanImage::VIEW_STORAGE, "RGBA8 BGRA storage did not select the identity storage view"); + Require("SampledColorViews", "storage uint BGRA write mapping", + SelectStorageColorView(VK_FORMAT_R8G8B8A8_UINT, VK_FORMAT_R8G8B8A8_UINT, + DstSel(6, 5, 4, 7)) == VulkanImage::VIEW_STORAGE, + "RGBA8_UINT BGRA storage did not select the identity storage view"); Require( "SampledColorViews", "mutable BGRA sRGB storage view", SelectStorageColorView(VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_R8G8B8A8_UNORM, @@ -8964,7 +8979,7 @@ void CheckSampledColorViews() { "GetModuleFileName failed"); for (const char *kind : {"sampled", "sampled-compatible-swizzle", "sampled-compatible-reverse", - "sampled-compatible-class", "sampled-compatible-colorspace", + "sampled-compatible-colorspace", "sampled-compatible-snorm", "sampled-rgb10-swizzle", "sampled-rgb10-reverse", "sampled-abgr-format", "sampled-depth-format", "sampled-depth-swizzle", "storage", "storage-format", @@ -9040,6 +9055,17 @@ void CheckSampledDepthResource() { Require("SampledDepthResource", "integer rejected", !IsSupportedSampledDepthResource(resource), "integer depth resource was accepted"); + Require("SampledDepthResource", "integer reinterpret resource", + IsSupportedSampledDepthUintResource(resource), + "read-only uint depth reinterpretation resource was rejected"); + Require("SampledDepthResource", "D32 to R32_UINT", + IsDepthUintTextureReinterpretation( + VK_FORMAT_D32_SFLOAT, + Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt), VK_FORMAT_R32_UINT) && + !IsDepthUintTextureReinterpretation( + VK_FORMAT_D16_UNORM, + Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt), VK_FORMAT_R32_UINT), + "depth uint reinterpretation format boundary changed"); std::printf("[host] %-32s ok\n", "SampledDepthResource"); } @@ -9074,6 +9100,53 @@ void CheckSampledDepthDescriptor() { Require("SampledDepthDescriptor", "non-depth tile", !IsSupportedDepthTargetDescriptor(descriptor, image), "non-depth texture descriptor was accepted"); + + DepthTargetInfo target{}; + target.address = 0x4a8c00000; + target.size = 0x100000; + target.width = 512; + target.height = 512; + target.pitch = 512; + target.layers = 1; + target.bytes_per_element = 4; + target.guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float); + target.format = VK_FORMAT_D32_SFLOAT; + target.tile_mode = Prospero::GpuEnumValue(Prospero::TileMode::kDepth); + ImageInfo expanded{}; + expanded.address = target.address; + expanded.size = 0x400000; + expanded.format = target.guest_format; + expanded.width = target.width; + expanded.height = target.height; + expanded.pitch = target.pitch; + expanded.levels = 1; + expanded.view_levels = 1; + expanded.depth = 4; + expanded.tile = target.tile_mode; + expanded.type = Prospero::GpuEnumValue(Prospero::ImageType::kColor2DArray); + Require("SampledDepthDescriptor", "PPSA01530 layer expansion", + IsSampledDepthExpansion(expanded, target), + "exact D32 per-layer expansion was rejected"); + auto invalid_expansion = expanded; + invalid_expansion.pitch *= 2; + const bool rejects_pitch = !IsSampledDepthExpansion(invalid_expansion, target); + invalid_expansion = expanded; + invalid_expansion.address++; + const bool rejects_address = !IsSampledDepthExpansion(invalid_expansion, target); + invalid_expansion = expanded; + invalid_expansion.levels = 2; + const bool rejects_mips = !IsSampledDepthExpansion(invalid_expansion, target); + invalid_expansion = expanded; + invalid_expansion.type = Prospero::GpuEnumValue(Prospero::ImageType::kColor2D); + const bool rejects_topology = !IsSampledDepthExpansion(invalid_expansion, target); + auto metadata_target = target; + metadata_target.htile_address = 0x4a9c00000; + metadata_target.htile_size = 0x10000; + const bool rejects_metadata = !IsSampledDepthExpansion(expanded, metadata_target); + Require("SampledDepthDescriptor", "expansion boundaries", + rejects_pitch && rejects_address && rejects_mips && rejects_topology && + rejects_metadata, + "unsupported depth expansion topology was accepted"); std::printf("[host] %-32s ok\n", "SampledDepthDescriptor"); } @@ -9144,6 +9217,17 @@ ShaderTextureResource BasicBgraStorageTextureDescriptor() { 0x00000000u, 0x00700000u, 0x00000000u, 0x00000000u}}; } +ShaderTextureResource Ppsa01530MaxMipStorageTextureDescriptor() { + return {{0x04a42900u, 0xc3e00000u, 0x000fc00fu, 0x91b0022cu, + 0x00000000u, 0x00700050u, 0x00000000u, 0x00000000u}}; +} + +ShaderRecompiler::IR::ImageResource Ppsa01530MaxMipStorageTextureResource() { + auto resource = BasicBgraStorageTextureResource(); + resource.kind = ShaderRecompiler::IR::ResourceKind::StorageImageUint; + return resource; +} + ShaderTextureResource Ppsa02527R16FloatStorageTextureDescriptor() { return {{0x00ce3500u, 0xc0d00000u, 0x010dc1dfu, 0x91b00204u, 0x00000000u, 0x00700000u, 0x00000000u, 0x00000000u}}; @@ -9255,6 +9339,11 @@ ShaderTextureResource BasicUintVolumeStorageTextureDescriptor() { resource = BasicArrayStorageTextureResource(); descriptor = BasicArrayStorageTextureDescriptor(); descriptor.fields[4] |= 1u << 16u; + } else if (std::strcmp(kind, "array-mip-view") == 0) { + resource = BasicArrayStorageTextureResource(); + descriptor = BasicArrayStorageTextureDescriptor(); + descriptor.fields[3] |= (1u << 12u) | (1u << 16u); + descriptor.fields[5] |= 1u << 4u; } else if (std::strcmp(kind, "reserved") == 0) { descriptor.fields[1] |= 1u << 29u; } else if (std::strcmp(kind, "metadata") == 0) { @@ -9308,6 +9397,26 @@ void CheckBasicStorageTextureDescriptor() { "PPSA02604 BGRA 2D storage descriptor fixture is malformed"); ValidateStorageTexture(BasicBgraStorageTextureResource(), bgra, 0x870000); + const auto max_mip = Ppsa01530MaxMipStorageTextureDescriptor(); + Require("BasicStorageTexture", "PPSA01530 max-mip descriptor", + max_mip.Base40() == 0x4a4290000ull && max_mip.Width5() + 1u == 64 && + max_mip.Height5() + 1u == 64 && max_mip.Depth() + 1u == 1 && + max_mip.BaseLevel() == 0 && max_mip.LastLevel() == 0 && + max_mip.MaxMip() == 5 && + max_mip.Format() == + Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32UInt) && + max_mip.TileMode() == + Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) && + max_mip.DstSelXYZW() == DstSel(4, 5, 0, 1), + "PPSA01530 max-mip storage descriptor fixture is malformed"); + ValidateStorageTexture(Ppsa01530MaxMipStorageTextureResource(), max_mip, 0x20000); + auto mip_one = max_mip; + mip_one.fields[3] |= (1u << 12u) | (1u << 16u); + Require("BasicStorageTexture", "PPSA01530 mip-one descriptor", + mip_one.BaseLevel() == 1 && mip_one.LastLevel() == 1 && mip_one.MaxMip() == 5, + "PPSA01530 mip-one storage descriptor fixture is malformed"); + ValidateStorageTexture(Ppsa01530MaxMipStorageTextureResource(), mip_one, 0x20000); + const auto r16_float = Ppsa02527R16FloatStorageTextureDescriptor(); Require("BasicStorageTexture", "PPSA02527 R16F descriptor", r16_float.Base40() == 0xce350000ull && r16_float.Width5() + 1u == 1920 && @@ -9404,6 +9513,16 @@ void CheckBasicStorageTextureDescriptor() { depth_tile.DstSelXYZW() == DstSel(4, 0, 0, 1), "PPSA14053 write-only depth-tile storage descriptor fixture is malformed"); ValidateStorageTexture(Ppsa14053DepthTileStorageTextureResource(), depth_tile, 0x10000); + Require("BasicStorageTexture", "D32-compatible R32_UINT depth tile", + IsSupportedStorageDepthTile( + Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt), + Prospero::GpuEnumValue(Prospero::ImageType::kColor2D), 64, 64, 1), + "write-only R32_UINT depth-tile recreation was rejected"); + Require("BasicStorageTexture", "R32_UINT replicated write mapping", + IsSupportedStorageSwizzle( + Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt), + DstSel(4, 4, 4, 4)), + "single-channel replicated destination selection was rejected"); char path[MAX_PATH]{}; Require("BasicStorageTexture", "host", @@ -9412,7 +9531,7 @@ void CheckBasicStorageTextureDescriptor() { for (const char *kind : {"resource", "type", "tile", "mip", "swizzle", "linear-rgb1-read", "bgra-read", "r16-float-read", "r8-unorm-read", "yzwx-read", "yzwx-format", - "array-base-view", "reserved", "metadata", "uint-format", + "array-base-view", "array-mip-view", "reserved", "metadata", "uint-format", "uint-resource-float-format", "depth-tile-read", "depth-tile-extent"}) { std::string command = std::string("\"") + path + @@ -9933,6 +10052,75 @@ void CheckRenderTargetTileRoundTrip() { std::printf("[host] %-32s ok\n", "RenderTargetTileRoundTrip"); } +void CheckStorageTextureMipTailRoundTrip() { + constexpr uint32_t format = + Prospero::GpuEnumValue(Prospero::BufferFormat::k32_32UInt); + ImageInfo info{}; + info.address = 0x4a4290000ull; + info.format = format; + info.width = 64; + info.height = 64; + info.pitch = TileGetTexturePitch( + format, info.width, 6, + Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget)); + info.levels = 6; + info.view_levels = 1; + info.tile = Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget); + info.depth = 1; + info.type = Prospero::GpuEnumValue(Prospero::ImageType::kColor2D); + TileSizeAlign total{}; + TileSizeOffset levels[16]{}; + TilePaddedSize padded[16]{}; + TileGetTextureSize(format, info.width, info.height, info.pitch, info.levels, + info.tile, &total, levels, padded); + info.size = total.size; + Require("StorageTextureMipTail", "layout", + info.pitch == 128 && info.size == 0x20000 && + levels[0].src_offset == 0x10000 && levels[0].src_size == 0x10000 && + levels[1].src_offset == 0 && levels[1].src_size == 0x10000, + "PPSA01530 mip-tail fixture disagrees with the PS5 layout"); + auto layout = TextureCalcUploadLayout( + format, info.width, info.height, info.levels, info.depth, info.pitch, + info.tile, info.size, true, false, false, "StorageTextureMipTailTest"); + auto regions = TextureBuildUploadRegions( + layout, VK_FORMAT_R32G32_UINT, info.width, info.height, info.depth, + info.levels, false, false, TextureUploadDestination::MipLevels, + TextureUploadSliceLayout::MipChainPerSlice); + std::vector linear(info.size, 0); + for (uint32_t level = 0; level < info.levels; level++) { + auto ®ion = regions[level]; + for (uint32_t y = 0; y < region.height; y++) { + auto *row = linear.data() + region.offset + + static_cast(y) * region.pitch * 8u; + for (uint32_t x = 0; x < region.width * 8u; x++) { + row[x] = static_cast((level * 43u + y * 17u + x * 7u) & 0xffu); + } + } + } + std::vector guest(info.size, 0xcd); + Tiler tiler; + tiler.TileImage(guest.data(), linear.data(), info); + std::vector restored(info.size, 0); + for (uint32_t level = 0; level < info.levels; level++) { + const auto ®ion = regions[level]; + const auto &level_size = layout.level_sizes[level]; + const auto guest_offset = level_size.src_size != 0 ? level_size.src_offset + : level_size.offset; + TileConvertTiledToLinearRenderTarget( + restored.data() + region.offset, guest.data() + guest_offset, + region.width, region.height, region.pitch, 8u, level_size.size, + level_size.src_size, level_size.x, level_size.y); + for (uint32_t y = 0; y < region.height; y++) { + const auto row = region.offset + static_cast(y) * region.pitch * 8u; + Require("StorageTextureMipTail", "contents", + std::memcmp(linear.data() + row, restored.data() + row, + region.width * 8u) == 0, + "multi-mip storage texture did not round-trip through its packed tail"); + } + } + std::printf("[host] %-32s ok\n", "StorageTextureMipTail"); +} + void CheckDepthTargetTileRoundTrip() { struct Case { uint32_t width; @@ -10247,6 +10435,28 @@ void CheckStorageTextureSampledReuse() { VK_FORMAT_R8G8B8A8_UNORM, true, false, true) == StorageSampledOverlap::ExactImage, "exact storage backing rejected a distinct sampled descriptor swizzle"); + ImageInfo depth_uint_storage{}; + depth_uint_storage.address = 0x4a5c90000; + depth_uint_storage.size = 0x10000; + depth_uint_storage.format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32UInt); + depth_uint_storage.width = 64; + depth_uint_storage.height = 64; + depth_uint_storage.pitch = 128; + depth_uint_storage.levels = 1; + depth_uint_storage.view_levels = 1; + depth_uint_storage.depth = 1; + depth_uint_storage.tile = Prospero::GpuEnumValue(Prospero::TileMode::kDepth); + depth_uint_storage.swizzle = DstSel(4, 4, 4, 4); + depth_uint_storage.type = image_2d; + auto depth_float_sampled = depth_uint_storage; + depth_float_sampled.format = Prospero::GpuEnumValue(Prospero::BufferFormat::k32Float); + depth_float_sampled.swizzle = DstSel(4, 0, 0, 1); + Require("StorageTextureSampledReuse", "PPSA01530 R32 view", + ClassifyStorageSampledOverlap(depth_float_sampled, depth_uint_storage, + VK_FORMAT_R32_SFLOAT, VK_FORMAT_R32_UINT, + true, false, true) == + StorageSampledOverlap::ExactImage, + "PPSA01530 GPU-owned R32_UINT storage image did not accept its exact R32_SFLOAT view"); const auto usage = TextureFormatUsage::Sampled | TextureFormatUsage::Storage; Require("StorageTextureSampledReuse", "usage", (TextureGetUsage(usage) & @@ -10598,6 +10808,8 @@ void CheckBufferImageWrites() { constexpr uint64_t video_size = 0x1fe0000ull; constexpr uint64_t target = 0x1158d80000ull; constexpr uint64_t target_size = 0x870000ull; + constexpr uint64_t storage_address = 0x4a4290000ull; + constexpr uint64_t storage_size = 0x20000ull; struct Case { const char *name; uint64_t buffer_address; @@ -10681,6 +10893,22 @@ void CheckBufferImageWrites() { Case{"target unaligned", target + 0x100, target_size, target + 0x100, target_size, BufferImageBinding::RenderTarget, true, true, BufferImageWrite::Unsupported}, + Case{"storage mip page", storage_address + 0x10000, 0x10000, storage_address, storage_size, + BufferImageBinding::StorageTexture, true, true, + BufferImageWrite::SynchronizeStorageTexture}, + Case{"storage unformatted", storage_address + 0x10000, 0x10000, storage_address, storage_size, + BufferImageBinding::StorageTexture, true, false, + BufferImageWrite::Unsupported}, + Case{"storage unaligned", storage_address + 0x100, 0x10000, storage_address, storage_size, + BufferImageBinding::StorageTexture, true, true, + BufferImageWrite::Unsupported}, + Case{"depth exact", 0x4a5c80000ull, 0x10000, 0x4a5c80000ull, 0x10000, + BufferImageBinding::DepthTarget, true, true, + BufferImageWrite::SynchronizeDepthTarget}, + Case{"depth partial", 0x4a5c80000ull, 0x8000, 0x4a5c80000ull, 0x10000, + BufferImageBinding::DepthTarget, true, true, BufferImageWrite::Unsupported}, + Case{"depth unformatted", 0x4a5c80000ull, 0x10000, 0x4a5c80000ull, 0x10000, + BufferImageBinding::DepthTarget, true, false, BufferImageWrite::Unsupported}, }; for (const auto &test : cases) { const auto actual = ClassifyBufferImageWrite( @@ -10689,6 +10917,80 @@ void CheckBufferImageWrites() { Require("BufferImageWrite", test.name, actual == test.expected, "buffer/image write classification mismatch"); } + Require("BufferImageWrite", "storage containing overwrite", + ClassifyBufferImageWrite(storage_address - 0x80000, 0x100000, storage_address, + storage_size, BufferImageBinding::StorageTexture, false, true, + true) == BufferImageWrite::InvalidateStorageTexture, + "formatted containing overwrite did not retain refreshable storage ownership"); + Require("BufferImageWrite", "storage containing overwrite GPU-owned", + ClassifyBufferImageWrite(storage_address - 0x80000, 0x100000, storage_address, + storage_size, BufferImageBinding::StorageTexture, true, true, + false) == BufferImageWrite::Unsupported, + "containing overwrite bypassed GPU-owned storage synchronization"); + Require("BufferImageWrite", "depth repeated buffer overwrite", + ClassifyBufferImageWrite(0x4a5cd0000ull, 0x10000, 0x4a5cd0000ull, 0x10000, + BufferImageBinding::DepthTarget, false, true, true) == + BufferImageWrite::InvalidateDepthTarget, + "buffer-owned exact depth overwrite was rejected"); + + // PPSA01530's packed mip0 page is first synchronized out of a GPU-owned storage image, + // written through a persistent formatted buffer, and then rebound as the same storage image. + const auto partial_refresh = ClassifyStorageBufferRebind( + true, false, true, false, false, true); + const auto refreshed_reuse = ClassifyStorageBufferRebind( + true, true, false, true, false, true); + Require("BufferImageWrite", "storage partial lifecycle", + partial_refresh == StorageBufferRebind::RefreshFromBacking && + refreshed_reuse == StorageBufferRebind::Reuse, + "storage-to-partial-buffer-to-storage lifecycle was rejected"); + Require("BufferImageWrite", "storage partial incoherent", + ClassifyStorageBufferRebind(true, false, true, false, false, false) == + StorageBufferRebind::Unsupported, + "incoherent partial storage backing was admitted"); + Require("BufferImageWrite", "storage contradictory ownership", + ClassifyStorageBufferRebind(true, false, true, true, false, true) == + StorageBufferRebind::Unsupported, + "contradictory storage tracker ownership was admitted"); + Require("BufferImageWrite", "storage mip-one cache miss", + SelectImageBackingBaseLevel(true, 1) == 0 && + SelectImageBackingBaseLevel(false, 1) == 1, + "storage backing creation remained dependent on first-bound view mip"); + Require("BufferImageWrite", "dynamic one-layer array view", + !NeedsStaticSampledArrayView(true, true) && + NeedsStaticSampledArrayView(true, false) && + !NeedsStaticSampledArrayView(false, false), + "dynamic sampled array view was replaced by a missing static view"); + + DepthTargetInfo old_depth{}; + old_depth.address = 0x4a5c90000ull; + old_depth.size = 0x10000; + old_depth.width = old_depth.height = 32; + old_depth.layers = 1; + DepthTargetInfo cleared_depth = old_depth; + cleared_depth.width = cleared_depth.height = 64; + cleared_depth.depth_load_clear = true; + Require("BufferImageWrite", "cleared depth reinterpretation", + ClassifyDepthTargetOverlap(old_depth, true, false, true, cleared_depth) == + DepthOverlap::DiscardTarget, + "exact cleared depth allocation could not discard its old native shape"); + cleared_depth.depth_load_clear = false; + Require("BufferImageWrite", "loaded depth reinterpretation", + ClassifyDepthTargetOverlap(old_depth, true, false, true, cleared_depth) == + DepthOverlap::Unsupported, + "loaded depth reinterpretation discarded live native contents"); + RenderTargetInfo color_alias{}; + color_alias.address = old_depth.address; + color_alias.size = old_depth.size; + Require("BufferImageWrite", "buffer-owned depth to color", + CanRetireBufferOwnedDepthForRenderTarget(old_depth, false, true, true, true, + color_alias) && + !CanRetireBufferOwnedDepthForRenderTarget(old_depth, true, false, true, true, + color_alias), + "depth-to-color recreation did not require coherent buffer ownership"); + Require("BufferImageWrite", "page-neighbor depth to color", + !CanRetireBufferOwnedDepthForRenderTarget(old_depth, false, true, true, false, + color_alias), + "same-page byte-disjoint buffer authorized depth-to-color recreation"); TileSizeAlign storage{}; Require("BufferImageWrite", "target layout", @@ -11763,6 +12065,14 @@ void CheckDepthTargetFootprints() { } void CheckHtileClearTargetResolution() { + HW::DepthDepthSizeXY one_pixel{}; + one_pixel.valid = true; + Require("HtileClearTargetResolution", "1x1 encoded depth extent", + one_pixel.valid && one_pixel.x_max + 1u == 1u && one_pixel.y_max + 1u == 1u, + "zero DB_DEPTH_SIZE_XY fields were mistaken for an absent register"); + HW::DepthDepthSizeXY absent{}; + Require("HtileClearTargetResolution", "absent depth extent", !absent.valid, + "an unwritten DB_DEPTH_SIZE_XY register was admitted"); HW::DepthRenderTarget descriptor_backed{}; descriptor_backed.z_info.format = Prospero::GpuEnumValue(Prospero::DepthFormat::kZ16); descriptor_backed.z_info.tile_surface_enable = true; @@ -12041,6 +12351,7 @@ int main(int argc, char **argv) { if (argc == 2 && std::strcmp(argv[1], "--layered-image-only") == 0) { CheckColorResolveLayers(); CheckRenderTargetTileRoundTrip(); + CheckStorageTextureMipTailRoundTrip(); CheckDepthTargetTileRoundTrip(); CheckGpuMetadataReuse(); return 0; @@ -12115,6 +12426,7 @@ int main(int argc, char **argv) { CheckColorResolveLayers(); CheckStandard64RenderTargetTileRoundTrip(); CheckRenderTargetTileRoundTrip(); + CheckStorageTextureMipTailRoundTrip(); CheckDepthTargetTileRoundTrip(); CheckStencilTargetTileRoundTrip(); CheckStorageTextureVolumeUploadLayout();