diff --git a/src/graphics/host_gpu/renderer/cache/textureCache.cpp b/src/graphics/host_gpu/renderer/cache/textureCache.cpp index 3e1bd690a..c27bbf6fa 100644 --- a/src/graphics/host_gpu/renderer/cache/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/cache/textureCache.cpp @@ -714,7 +714,8 @@ ImageId TextureCache::ResolveDepthOverlap(const ImageInfo& requested, BindingTyp cached.info.data.size / cached.info.resources.layers && !requested.HasStencil() && !cached.info.HasStencil() && !requested.HasMetadata() && !cached.info.HasMetadata(); - bool recreate = cached.info.resources < requested.resources; + bool recreate = cached.info.resources < requested.resources || + requested.IsVolume() != cached.info.IsVolume(); switch (binding) { case BindingType::Texture: recreate |= requested.IsDepth() && !cached.info.IsDepth(); diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp index 44c645b7f..318c74652 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp @@ -876,6 +876,7 @@ void RenderExecutor::RebindImages(PreparedBindings& prepared) { auto& images = prepared.images; EXIT_IF(images.size() != program.info.images.size()); auto& texture_cache = m_context.GetTextureCache(); + // Acquire each view before a later overlapping descriptor can replace its image. for (uint32_t i = 0; i < program.info.images.size(); i++) { const auto old_image = texture_cache.m_slot_images.try_get(images[i].image_id); if (old_image == nullptr || (!old_image->registered && !old_image->info.data.Empty()) || @@ -887,8 +888,6 @@ void RenderExecutor::RebindImages(PreparedBindings& prepared) { BindImage(images[i].image_id, images[i].desc.type == TextureCache::BindingType::Storage); } - } - for (uint32_t i = 0; i < program.info.images.size(); i++) { auto& binding = images[i]; binding.mip_views.clear(); const auto& resource = program.info.images[i]; diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index e84f71590..000896c1e 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -11507,7 +11507,7 @@ public: } { - const auto buffer_program = make_buffer_program(ShaderType::Compute, {.read = true}); + const auto buffer_program = make_buffer_program(ShaderType::Compute, {.read = true, .written = true}); constexpr uint64_t buffer_address = base + 0xe0002; ShaderBufferResource descriptor{}; descriptor.UpdateAddress48(buffer_address); @@ -11532,6 +11532,90 @@ public: "native binding lost the byte adjustment or rounded the final halfword range"); } + for (const auto format : {Prospero::BufferFormat::k8UNorm, + Prospero::BufferFormat::k16UNorm}) { + const uint64_t alias_address = base + 0xe00000 + + Prospero::NumBytesPerElement(format) * 0x10000; + constexpr uint8_t texel = 0x7f; + Libs::LibKernel::Memory::WriteBacking(alias_address, &texel, sizeof(texel)); + u32 expected = texel; + if (format == Prospero::BufferFormat::k16UNorm) { + auto depth_desc = MakeLinearDesc(alias_address, 256, vk::Format::eD16Unorm, + format, Prospero::ImageType::kColor2D, {1, 1, 1}, 1, 2, 1); + depth_desc.info.pitch = 128; + depth_desc.info.mip_layout[0] = {0, 256, 128, 1}; + depth_desc.type = BindingType::DepthTarget; + depth_desc.view_info.aspect = vk::ImageAspectFlagBits::eDepth; + depth_desc.view_info.usage = vk::ImageUsageFlagBits::eDepthStencilAttachment; + const auto depth_id = texture_cache.FindImage(depth_desc); + auto &depth = texture_cache.GetImage(depth_id); + depth.Transit(vk::ImageLayout::eTransferDstOptimal, + vk::AccessFlagBits2::eTransferWrite, {}, scheduler.Current().Handle()); + const vk::ImageSubresourceRange range{vk::ImageAspectFlagBits::eDepth, 0, 1, 0, 1}; + scheduler.Current().Handle().clearDepthStencilImage(depth.backing.image, + vk::ImageLayout::eTransferDstOptimal, vk::ClearDepthStencilValue{1.f, 0}, range); + texture_cache.MarkGpuWritten(depth_id); + expected = 0xffff; + } + ShaderRecompiler::IR::CompiledShaderInfo program{}; + program.stage = ShaderType::Compute; + ShaderRecompiler::IR::ResourceSnapshot snapshot; + // PPSA24156 binds one-texel R8/R16 allocations as 3D, then 2D. + for (const auto type : {Prospero::ImageType::kColor3D, + Prospero::ImageType::kColor2D}) { + ShaderRecompiler::IR::ImageResource resource{}; + resource.resource_class = ShaderRecompiler::IR::ImageResourceClass::Sampled; + resource.read = true; + resource.numeric_class = Prospero::TextureNumericClass::Float; + resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim2D; + if (type == Prospero::ImageType::kColor3D) { + resource.dimension = ShaderRecompiler::Decoder::ImageDimension::Dim3D; + } + program.info.images.push_back(resource); + auto &value = snapshot.images.emplace_back(); + value.dword_count = 8; + value.dwords = {static_cast(alias_address >> 8u), + static_cast(format) << 20u, 0, + DstSel(4, 4, 4, 1) | (static_cast(type) << 28u), + 0, 0x00700000u, 0, 0}; + } + ShaderStageRuntime runtime{&program, &snapshot}; + PreparedBindings bindings; + executor.PrepareBindings(runtime, bindings); + Require(name, "overlapping dimension discovery", + !texture_cache.GetImage(bindings.images[0].image_id).registered, + "the 2D descriptor did not replace its earlier 3D alias"); + executor.RebindImages(bindings); + auto output = CreateHostBuffer(name, 8, vk::BufferUsageFlagBits::eTransferDst, {0, 0}); + constexpr std::array image_types{vk::ImageType::e3D, vk::ImageType::e2D}; + for (u32 i = 0; i < bindings.images.size(); ++i) { + const auto &binding = bindings.images[i]; + auto &image = texture_cache.GetImage(binding.image_id); + Require(name, "overlapping dimension views", binding.image_view != nullptr && + image.backing.image_type == image_types[i], + "rediscovery lost a live view or the descriptor's image dimension"); + image.Transit(vk::ImageLayout::eTransferSrcOptimal, + vk::AccessFlagBits2::eTransferRead, {}, scheduler.Current().Handle()); + vk::BufferImageCopy copy{}; + copy.bufferOffset = i * sizeof(u32); + copy.imageSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, 1}; + copy.imageExtent = {1, 1, 1}; + scheduler.Current().Handle().copyImageToBuffer(image.backing.image, + vk::ImageLayout::eTransferSrcOptimal, output.buffer, 1, ©); + } + vk::MemoryBarrier barrier{}; + barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite; + barrier.dstAccessMask = vk::AccessFlagBits::eHostRead; + scheduler.Current().Handle().pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, + vk::PipelineStageFlagBits::eHost, {}, 1, &barrier, 0, nullptr, 0, nullptr); + RenderExecutorTestAccess::ResetBindings(executor); + scheduler.Finish(); + Require(name, "overlapping dimension GPU contents", + ReadBuffer(name, output, 2) == std::vector{expected, expected}, + "an alias replacement retired image contents before queued GPU reads"); + DestroyBuffer(&output); + } + constexpr auto stencil_format = Prospero::BufferFormat::k8UInt; constexpr auto linear = Prospero::TileMode::kLinear; TileSizeAlign stencil_layout{}; @@ -39529,6 +39613,11 @@ int main(int argc, char **argv) { CheckImageTransitionState(vulkan.RuntimeRenderer()); return 0; } + if (argc == 2 && std::strcmp(argv[1], "--image-rebind-only") == 0) { + VulkanHarness vulkan; + vulkan.CheckRenderExecutorStencilBindingDiscovery(); + return 0; + } #if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS if (argc == 2 && std::strcmp(argv[1], "--reverse-rt-death") == 0) { RunReverseRenderTargetDeathCase();