diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp index aeaf94728..d986ddf19 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp @@ -514,19 +514,50 @@ static ImageViewInfo TextureViewInfo(const ShaderRecompiler::IR::ImageResource& return view; } -static bool TextureViewPreservesMipLayout(const TileSurfaceDescription& description, - uint32_t view_levels) { +static bool ResolveTextureMipView(const TileSurfaceDescription& description, bool metadata, + uint32_t view_levels, uint32_t& levels, uint32_t& base_level) { TileSurfaceLayout physical {}; TileSurfaceLayout view {}; auto view_description = description; - view_description.levels = view_levels; - return TileGetTiledTextureLayout(description, physical) && - TileGetTiledTextureLayout(view_description, view) && - physical.first_tail_level == view.first_tail_level && - physical.block_slice_size == view.block_slice_size && - physical.total_size == view.total_size && - std::equal(std::begin(physical.mips), std::begin(physical.mips) + description.levels, - std::begin(view.mips)); + view_description.levels = levels; + if (!TileGetTiledTextureLayout(description, physical) || + !TileGetTiledTextureLayout(view_description, view)) { + return false; + } + if (physical.first_tail_level == view.first_tail_level && + physical.block_slice_size == view.block_slice_size && + physical.total_size == view.total_size && + std::equal(std::begin(physical.mips), std::begin(physical.mips) + description.levels, + std::begin(view.mips))) { + return true; + } + if (metadata || ((description.layers > 1 || description.depth > 1) && + physical.block_slice_size != view.block_slice_size)) { + return false; + } + // T# addresses the last mip. A view can select the same stored subresources + // with different mip indices; inaccessible mips need no host representation. + for (uint32_t base = 0; base + view_levels <= description.levels; ++base) { + bool matches = true; + for (uint32_t i = 0; i < view_levels; ++i) { + const auto source = base_level + i; + const auto target = base + i; + if (physical.mips[target] != view.mips[source] || + (target >= physical.first_tail_level) != (source >= view.first_tail_level) || + std::max(description.width >> target, 1u) != std::max(description.width >> source, 1u) || + std::max(description.height >> target, 1u) != std::max(description.height >> source, 1u) || + std::max(description.depth >> target, 1u) != std::max(description.depth >> source, 1u)) { + matches = false; + break; + } + } + if (matches) { + levels = description.levels; + base_level = base; + return true; + } + } + return false; } TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageResource& resource, @@ -560,7 +591,7 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR const auto view_levels = multisampled || single_storage_mip ? 1u : static_cast(last_level - base_level) + 1u; - const auto levels = + auto levels = multisampled ? 1u : std::max(physical_levels, base_level + view_levels); const auto tile = descriptor.TileMode(); const bool depth_tile = tile == Prospero::TileMode::kDepth; @@ -602,12 +633,13 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR type == Prospero::ImageType::kColor2DArray || type == Prospero::ImageType::kColor2DMsaaArray; const auto image_layers = layered ? depth : 1u; + auto view_base = static_cast(base_level); if (levels > physical_levels) { const TileSurfaceDescription physical { format, tile, volume ? TileSurfaceDimension::Dim3D : TileSurfaceDimension::Dim2D, width, height, volume ? depth : 1u, physical_levels, image_layers}; - // Texture mip views take precedence over the resource count, but must keep its storage layout. - if (!TextureViewPreservesMipLayout(physical, levels)) { + if (!ResolveTextureMipView(physical, !resource.r128 && descriptor.MetaCompress(), + view_levels, levels, view_base)) { EXIT("unsupported texture mip view changes physical layout: base=%u last=%u max=%u " "extent=%ux%ux%u tile=%u\n", base_level, last_level, max_mip, width, height, depth, @@ -679,6 +711,7 @@ TextureBinding RenderExecutor::ResolveTexture(const ShaderRecompiler::IR::ImageR } desc.view_info = TextureViewInfo(resource, descriptor, view_format, surface_format, storage, view_levels, desc.info.resources.layers); + desc.view_info.base_level = view_base; desc.type = storage ? TextureCache::BindingType::Storage : TextureCache::BindingType::Texture; auto id = texture_cache.FindImage(desc, shader_conversion); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index eb73f35f8..85a5dcb58 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -10127,18 +10127,20 @@ public: program.shader_info_complete = true; ShaderRecompiler::IR::AllocateBindings(program); }; + const auto make_buffer_program = + [](ShaderType stage, ShaderRecompiler::IR::BufferResource resource) { + ShaderRecompiler::IR::CompiledShaderInfo program{}; + program.stage = stage; + program.info.buffers.push_back(resource); + program.bindings.descriptors.push_back( + {ShaderRecompiler::IR::DescriptorBindingKind::Buffers, {0}}); + program.bindings.memory_offset_count = 1; + program.bindings.push_data_start_dword = 0; + return program; + }; { - ShaderRecompiler::IR::Program buffer_ir{}; - buffer_ir.stage = ShaderType::Compute; - buffer_ir.resource_tracking_complete = true; - buffer_ir.info.buffers.resize(1); - buffer_ir.info.buffers[0].read = true; - allocate_bindings(buffer_ir); - ShaderRecompiler::IR::CompiledShaderInfo buffer_program{}; - buffer_program.stage = buffer_ir.stage; - buffer_program.info = std::move(buffer_ir.info); - buffer_program.bindings = std::move(buffer_ir.bindings); + const auto buffer_program = make_buffer_program(ShaderType::Compute, {.read = true}); constexpr uint64_t buffer_address = base + allocation_size - 0x5000; ShaderBufferResource buffer_descriptor{}; @@ -10380,6 +10382,60 @@ public: Prospero::TextureNumericClass::Uint; sampled_overwide_resource.read = true; sampled_overwide_resource.written = false; + { + // MLB The Show 21 compresses a standalone final block through mip 9. + constexpr uint64_t address = base + 0x180000; + ShaderRecompiler::IR::DescriptorValue descriptor{}; + descriptor.dwords = {static_cast(address >> 8u), + 0x03e00000u, 0, 0x9019902cu, 0, 0x00700000u, 0, 0}; + descriptor.dword_count = 8; + TestCase test; + test.name = "RebasedLastMipQueryStore"; + test.has_user_data = true; + std::copy_n(descriptor.dwords.begin(), 8, test.user_data.begin()); + test.opcodes = {ShaderOpcode::V_MOV_B32, ShaderOpcode::IMAGE_GET_RESINFO, + ShaderOpcode::IMAGE_STORE, ShaderOpcode::S_ENDPGM}; + AppendVMovU32(&test.code, 24, 0); + AppendVMovU32(&test.code, 25, 0); + test.code.push_back(EncodeMimg0(0x0e, 0x3)); + test.code.push_back(EncodeMimg1(0, 24)); + test.code.push_back(EncodeMimg0(0x08, 0x3)); + test.code.push_back(EncodeMimg1(0, 24)); + AppendEnd(&test.code); + const auto compiled = CompileCase(test, SubgroupSize()); + auto sampled_descriptor = descriptor; + sampled_descriptor.dwords[1] |= 9u << 16u; + const auto query = RenderExecutorTestAccess::ResolveTexture( + executor, compiled.program.info.images.at(0), sampled_descriptor); + const auto store = RenderExecutorTestAccess::ResolveTexture( + executor, compiled.program.info.images.at(1), descriptor); + Require(name, "rebased last mip", + query.image_id == store.image_id && + query.desc.info.data.size == 256 && + query.desc.info.resources.levels == 1 && + query.desc.view_info.base_level == 0 && + query.desc.view_info.level_count == 1 && + query.desc.view_info.min_lod == 0 && + store.desc.view_info.base_level == 0, + "equivalent selected mip changed backing or relative LOD"); + Image sampled; + sampled.view = texture_cache.FindTexture(query.image_id, query.desc); + sampled.layout = vk::ImageLayout::eGeneral; + Image storage_view; + storage_view.view = texture_cache.FindTexture(store.image_id, store.desc); + storage_view.layout = vk::ImageLayout::eGeneral; + texture_cache.GetImage(store.image_id).Transit( + vk::ImageLayout::eGeneral, + vk::AccessFlagBits2::eShaderRead | vk::AccessFlagBits2::eShaderWrite, + {}, scheduler.Current().Handle()); + scheduler.Finish(); + Dispatch(test, compiled, {}, nullptr, &sampled, nullptr, &storage_view); + texture_cache.MarkGpuWritten(store.image_id); + Require(name, "rebased mip query and store", + ReadCachedTexel(name, context, store.image_id) == + std::vector{1, 1}, + "query/store did not access the standalone 1x1 mip"); + } auto plain_mipped_storage_binding = RenderExecutorTestAccess::ResolveTexture(executor, storage_resource, mipped_storage_descriptor); @@ -11937,17 +11993,8 @@ public: texture_cache, scheduler.Current(), expanded_array_id, {vk::ImageAspectFlagBits::eColor, 0, 1, 1, 1}, clear); - ShaderRecompiler::IR::Program buffer_ir{}; - buffer_ir.stage = ShaderType::Vertex; - buffer_ir.resource_tracking_complete = true; - buffer_ir.info.buffers.resize(1); - buffer_ir.info.buffers[0].read = true; - buffer_ir.info.buffers[0].formatted = true; - allocate_bindings(buffer_ir); - ShaderRecompiler::IR::CompiledShaderInfo buffer_program{}; - buffer_program.stage = buffer_ir.stage; - buffer_program.info = std::move(buffer_ir.info); - buffer_program.bindings = std::move(buffer_ir.bindings); + const auto buffer_program = make_buffer_program( + ShaderType::Vertex, {.read = true, .formatted = true}); constexpr uint64_t buffer_size = 2 * target_mip_size; static_assert(buffer_size > BufferCache::CACHING_PAGESIZE); @@ -12613,17 +12660,8 @@ public: } auto plane_upload = CreateHostBuffer(name, plane_size, vk::BufferUsageFlagBits::eTransferSrc, plane_words); - ShaderRecompiler::IR::Program plane_ir{}; - plane_ir.stage = ShaderType::Compute; - plane_ir.resource_tracking_complete = true; - auto &plane_resource = plane_ir.info.buffers.emplace_back(); - plane_resource.written = true; - plane_resource.formatted = true; - allocate_bindings(plane_ir); - ShaderRecompiler::IR::CompiledShaderInfo plane_program{}; - plane_program.stage = plane_ir.stage; - plane_program.info = std::move(plane_ir.info); - plane_program.bindings = std::move(plane_ir.bindings); + const auto plane_program = make_buffer_program( + ShaderType::Compute, {.written = true, .formatted = true}); ShaderBufferResource plane_descriptor{}; plane_descriptor.UpdateAddress48(plane_address); plane_descriptor.fields[1] |= 4u << 16u;