From bda49e810b5955bff8474e7b8f2b09eab51e9be2 Mon Sep 17 00:00:00 2001 From: Sssnowyyy <78863999+Sssnowyyy@users.noreply.github.com> Date: Mon, 2 Nov 2026 11:28:52 -0500 Subject: shader: evaluate handle programs for image and texture buffer descriptors 561f87abec only gave TextureDescriptor a handle_program. Image load/store/atomics and texture/image buffers whose handle comes from a storage buffer still got the placeholder constant buffer address (index 0, offset 0), so the pipelines read cbuf 0 word 0 as the handle (and asserted if cbuf 0 was unbound). Add handle_program to TextureBufferDescriptor, ImageBufferDescriptor and ImageDescriptor, compare it when merging, and let all four pipelines evaluate it for every descriptor kind instead of only TextureDescriptor. ImageQueryDimensions on such a handle was always declared Color2D, so array, cube and 3D views got a 2D declaration. The instruction carries no type, so take it from another instruction using the same handle program, falling back to Color2D when there is none. Follow-up to 561f87abec. The OpenGL pipeline edits are the same change as the Vulkan ones but were not compiled here. Assisted-by: Claude Sonnet 5.5 (implementation and commit message) --- src/shader_recompiler/ir_opt/texture_pass.cpp | 34 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) (limited to 'src/shader_recompiler/ir_opt/texture_pass.cpp') diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 514cd9b2b6..db8f1ac716 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -345,7 +345,8 @@ static inline u32 GetTextureHandleCached(Environment& env, const ConstBufferAddr // Cached variants of existing helpers // A handle computed per draw has no descriptor to read at translation time: assume an ordinary -// floating-point 2D texture, the instruction's own type deciding the rest. +// floating-point 2D texture, the instruction's own type deciding the rest (see the TexturePass +// ImageQueryDimensions case, which has none). static inline TextureType ReadTextureTypeCached(Environment& env, const ConstBufferAddr& cbuf) { if (cbuf.handle_program != ~0U) return TextureType::Color2D; return env.ReadTextureType(GetTextureHandleCached(env, cbuf)); @@ -1010,7 +1011,8 @@ public: desc.secondary_cbuf_offset == existing.secondary_cbuf_offset && desc.secondary_shift_left == existing.secondary_shift_left && desc.count == existing.count && desc.size_shift == existing.size_shift && - desc.has_secondary == existing.has_secondary; + desc.has_secondary == existing.has_secondary && + desc.handle_program == existing.handle_program; }); } @@ -1018,7 +1020,8 @@ public: const u32 index{Add(image_buffer_descriptors, desc, [&desc](const auto& existing) { return desc.format == existing.format && desc.cbuf_index == existing.cbuf_index && desc.cbuf_offset == existing.cbuf_offset && desc.count == existing.count && - desc.size_shift == existing.size_shift; + desc.size_shift == existing.size_shift && + desc.handle_program == existing.handle_program; })}; image_buffer_descriptors[index].is_written |= desc.is_written; image_buffer_descriptors[index].is_read |= desc.is_read; @@ -1050,7 +1053,8 @@ public: return desc.type == existing.type && desc.format == existing.format && desc.cbuf_index == existing.cbuf_index && desc.cbuf_offset == existing.cbuf_offset && desc.count == existing.count && - desc.size_shift == existing.size_shift; + desc.size_shift == existing.size_shift && + desc.handle_program == existing.handle_program; })}; image_descriptors[index].is_written |= desc.is_written; image_descriptors[index].is_read |= desc.is_read; @@ -1186,6 +1190,18 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo has_last_is_integer = true; return last_is_integer; }}; + // A handle program has no descriptor to read a type from, and ImageQueryDimensions carries + // none: it takes the type of another instruction using the same handle. + std::unordered_map handle_program_types; + for (const TextureInst& texture_inst : to_replace) { + const IR::Opcode opcode{IndexedInstruction(*texture_inst.inst)}; + if (texture_inst.cbuf.handle_program != ~0U && opcode != IR::Opcode::ImageQueryDimensions && + !IsStorageImageOpcode(opcode)) { + handle_program_types.try_emplace( + texture_inst.cbuf.handle_program, + texture_inst.inst->Flags().type.Value()); + } + } for (TextureInst& texture_inst : to_replace) { // TODO: Handle arrays IR::Inst* const inst{texture_inst.inst}; @@ -1196,7 +1212,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo bool is_multisample{false}; switch (inst->GetOpcode()) { case IR::Opcode::ImageQueryDimensions: - flags.type.Assign(ReadTextureTypeCached(env, cbuf)); + if (const auto it{handle_program_types.find(cbuf.handle_program)}; + it != handle_program_types.end()) { + flags.type.Assign(it->second); + } else { + flags.type.Assign(ReadTextureTypeCached(env, cbuf)); + } inst->SetFlags(flags); break; case IR::Opcode::ImageSampleImplicitLod: @@ -1261,6 +1282,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo .cbuf_offset = cbuf.offset, .count = count, .size_shift = size_shift, + .handle_program = cbuf.handle_program, }); } else { index = descriptors.Add(ImageDescriptor{ @@ -1273,6 +1295,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo .cbuf_offset = cbuf.offset, .count = count, .size_shift = size_shift, + .handle_program = cbuf.handle_program, }); } break; @@ -1289,6 +1312,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo .secondary_shift_left = cbuf.secondary_shift_left, .count = count, .size_shift = size_shift, + .handle_program = cbuf.handle_program, }); } else { count = std::min(count, sampled_dynamic_cap); -- cgit v1.2.3