diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index f0cf15af0..5615c3224 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp @@ -259,6 +259,14 @@ void SetupCapabilities(const Info& info, const Profile& profile, EmitContext& ct ctx.AddCapability(spv::Capability::ImageReadWriteLodAMD); } } + if (info.has_cube_arrays) { + if (info.has_storage_cube_arrays) { + // Implies SampledCubeArray + ctx.AddCapability(spv::Capability::ImageCubeArray); + } else { + ctx.AddCapability(spv::Capability::SampledCubeArray); + } + } if (info.has_texel_buffers) { ctx.AddCapability(spv::Capability::SampledBuffer); } diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 68a8182e5..12fd38d9a 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -185,12 +185,17 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, u32 handle, Id lod return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[1]), zero, zero, mips()); case AmdGpu::ImageType::Color1DArray: case AmdGpu::ImageType::Color2D: - case AmdGpu::ImageType::Cube: case AmdGpu::ImageType::Color2DMsaa: return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips()); case AmdGpu::ImageType::Color2DArray: case AmdGpu::ImageType::Color3D: return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[3]), mips()); + case AmdGpu::ImageType::Cube: + // Cube arrays do not have their own type to distinguish by. + if (texture.is_array) { + return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[3]), mips()); + } + return ctx.OpCompositeConstruct(ctx.U32[4], query(ctx.U32[2]), zero, mips()); default: UNREACHABLE_MSG("SPIR-V Instruction"); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 5df5f4ee4..06f9853a4 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -820,6 +820,7 @@ void EmitContext::DefineImagesAndSamplers() { .bound_type = image_desc.GetBoundType(sharp), .is_integer = is_integer, .is_storage = is_storage, + .is_array = image_desc.is_array, }); interfaces.push_back(id); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index 80d0d4d9f..80b0bc432 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -225,6 +225,7 @@ public: AmdGpu::ImageType bound_type; bool is_integer = false; bool is_storage = false; + bool is_array = false; }; struct BufferDefinition { diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index c5be08b7d..a5b54dff7 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -418,6 +418,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) { IR::TextureInstInfo info{}; info.has_lod.Assign(has_mip); + info.is_array.Assign(mimg.da); const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info); for (u32 i = 0; i < 4; i++) { @@ -442,6 +443,7 @@ void Translator::IMAGE_STORE(bool has_mip, const GcnInst& inst) { IR::TextureInstInfo info{}; info.has_lod.Assign(has_mip); + info.is_array.Assign(mimg.da); boost::container::static_vector comps; for (u32 i = 0; i < 4; i++) { @@ -456,13 +458,18 @@ void Translator::IMAGE_STORE(bool has_mip, const GcnInst& inst) { } void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) { + const auto& mimg = inst.control.mimg; IR::VectorReg dst_reg{inst.dst[0].code}; const IR::ScalarReg tsharp_reg{inst.src[2].code * 4}; const auto flags = ImageResFlags(inst.control.mimg.dmask); const bool has_mips = flags.test(ImageResComponent::MipCount); const IR::U32 lod = ir.GetVectorReg(IR::VectorReg(inst.src[0].code)); const IR::Value tsharp = ir.GetScalarReg(tsharp_reg); - const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips)); + + IR::TextureInstInfo info{}; + info.is_array.Assign(mimg.da); + + const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips), info); if (flags.test(ImageResComponent::Width)) { ir.SetVectorReg(dst_reg++, IR::U32{ir.CompositeExtract(size, 0)}); @@ -484,6 +491,9 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) { IR::VectorReg addr_reg{inst.src[0].code}; const IR::ScalarReg tsharp_reg{inst.src[2].code * 4}; + IR::TextureInstInfo info{}; + info.is_array.Assign(mimg.da); + const IR::Value value = ir.GetVectorReg(val_reg); const IR::Value handle = ir.GetScalarReg(tsharp_reg); const IR::Value body = @@ -494,25 +504,25 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) { case AtomicOp::Swap: return ir.ImageAtomicExchange(handle, body, value, {}); case AtomicOp::Add: - return ir.ImageAtomicIAdd(handle, body, value, {}); + return ir.ImageAtomicIAdd(handle, body, value, info); case AtomicOp::Smin: - return ir.ImageAtomicIMin(handle, body, value, true, {}); + return ir.ImageAtomicIMin(handle, body, value, true, info); case AtomicOp::Umin: - return ir.ImageAtomicUMin(handle, body, value, {}); + return ir.ImageAtomicUMin(handle, body, value, info); case AtomicOp::Smax: - return ir.ImageAtomicIMax(handle, body, value, true, {}); + return ir.ImageAtomicIMax(handle, body, value, true, info); case AtomicOp::Umax: - return ir.ImageAtomicUMax(handle, body, value, {}); + return ir.ImageAtomicUMax(handle, body, value, info); case AtomicOp::And: - return ir.ImageAtomicAnd(handle, body, value, {}); + return ir.ImageAtomicAnd(handle, body, value, info); case AtomicOp::Or: - return ir.ImageAtomicOr(handle, body, value, {}); + return ir.ImageAtomicOr(handle, body, value, info); case AtomicOp::Xor: - return ir.ImageAtomicXor(handle, body, value, {}); + return ir.ImageAtomicXor(handle, body, value, info); case AtomicOp::Inc: - return ir.ImageAtomicInc(handle, body, value, {}); + return ir.ImageAtomicInc(handle, body, value, info); case AtomicOp::Dec: - return ir.ImageAtomicDec(handle, body, value, {}); + return ir.ImageAtomicDec(handle, body, value, info); default: UNREACHABLE(); } @@ -643,11 +653,14 @@ void Translator::IMAGE_GET_LOD(const GcnInst& inst) { IR::VectorReg addr_reg{inst.src[0].code}; const IR::ScalarReg tsharp_reg{inst.src[2].code * 4}; + IR::TextureInstInfo info{}; + info.is_array.Assign(mimg.da); + const IR::Value handle = ir.GetScalarReg(tsharp_reg); const IR::Value body = ir.CompositeConstruct( ir.GetVectorReg(addr_reg), ir.GetVectorReg(addr_reg + 1), ir.GetVectorReg(addr_reg + 2), ir.GetVectorReg(addr_reg + 3)); - const IR::Value lod = ir.ImageQueryLod(handle, body, {}); + const IR::Value lod = ir.ImageQueryLod(handle, body, info); ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(lod, 0)}); ir.SetVectorReg(dst_reg++, IR::F32{ir.CompositeExtract(lod, 1)}); } diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index 963faab8b..006dfe15e 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -73,7 +73,7 @@ struct ImageResource { bool is_read{}; bool is_written{}; - AmdGpu::ImageType GetBoundType(const AmdGpu::Image& image) const noexcept { + [[nodiscard]] AmdGpu::ImageType GetBoundType(const AmdGpu::Image& image) const noexcept { const auto base_type = image.GetType(); if (base_type == AmdGpu::ImageType::Color1DArray && !is_array) { return AmdGpu::ImageType::Color1D; @@ -206,6 +206,8 @@ struct Info { u64 pgm_hash{}; VAddr pgm_base; bool has_storage_images{}; + bool has_cube_arrays{}; + bool has_storage_cube_arrays{}; bool has_image_buffers{}; bool has_texel_buffers{}; bool has_discard{}; diff --git a/src/shader_recompiler/ir/ir_emitter.cpp b/src/shader_recompiler/ir/ir_emitter.cpp index 823f9bdcd..9b1c5f060 100644 --- a/src/shader_recompiler/ir/ir_emitter.cpp +++ b/src/shader_recompiler/ir/ir_emitter.cpp @@ -1732,11 +1732,6 @@ Value IREmitter::ImageGatherDref(const Value& handle, const Value& coords, const return Inst(Opcode::ImageGatherDref, Flags{info}, handle, coords, offset, dref); } -Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, - const IR::U1& skip_mips) { - return Inst(Opcode::ImageQueryDimensions, handle, lod, skip_mips); -} - Value IREmitter::ImageQueryDimension(const Value& handle, const IR::U32& lod, const IR::U1& skip_mips, TextureInstInfo info) { return Inst(Opcode::ImageQueryDimensions, Flags{info}, handle, lod, skip_mips); diff --git a/src/shader_recompiler/ir/ir_emitter.h b/src/shader_recompiler/ir/ir_emitter.h index 9aab9459b..29708baab 100644 --- a/src/shader_recompiler/ir/ir_emitter.h +++ b/src/shader_recompiler/ir/ir_emitter.h @@ -324,8 +324,6 @@ public: const F32& dref, const F32& lod, const Value& offset, TextureInstInfo info); - [[nodiscard]] Value ImageQueryDimension(const Value& handle, const U32& lod, - const U1& skip_mips); [[nodiscard]] Value ImageQueryDimension(const Value& handle, const U32& lod, const U1& skip_mips, TextureInstInfo info); diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index f7040ad75..fa17916e7 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -161,7 +161,7 @@ public: u32 Add(const ImageResource& desc) { const u32 index{Add(image_resources, desc, [&desc](const auto& existing) { - return desc.sharp_idx == existing.sharp_idx; + return desc.sharp_idx == existing.sharp_idx && desc.is_array == existing.is_array; })}; auto& image = image_resources[index]; image.is_read |= desc.is_read; @@ -691,7 +691,8 @@ void PatchImageSampleArgs(IR::Block& block, IR::Inst& inst, Info& info, // Query dimensions of image if needed for normalization. // We can't use the image sharp because it could be bound to a different image later. const auto dimensions = - unnormalized ? ir.ImageQueryDimension(handle, ir.Imm32(0u), ir.Imm1(false)) : IR::Value{}; + unnormalized ? ir.ImageQueryDimension(handle, ir.Imm32(0u), ir.Imm1(false), inst_info) + : IR::Value{}; const auto get_coord = [&](u32 coord_idx, u32 dim_idx) -> IR::Value { const auto coord = get_addr_reg(coord_idx); if (unnormalized) { diff --git a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp index c34b59b88..4c265b3e0 100644 --- a/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp +++ b/src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp @@ -5,7 +5,7 @@ namespace Shader::Optimization { -void Visit(Info& info, IR::Inst& inst) { +void Visit(Info& info, const IR::Inst& inst) { switch (inst.GetOpcode()) { case IR::Opcode::GetAttribute: case IR::Opcode::GetAttributeU32: @@ -87,6 +87,16 @@ void Visit(Info& info, IR::Inst& inst) { } } +void VisitImage(Info& info, const ImageResource& image) { + const auto sharp = image.GetSharp(info); + if (image.GetBoundType(sharp) == AmdGpu::ImageType::Cube && image.is_array) { + info.has_cube_arrays = true; + if (image.IsStorage(sharp)) { + info.has_storage_cube_arrays = true; + } + } +} + void CollectShaderInfoPass(IR::Program& program) { Info& info{program.info}; for (IR::Block* const block : program.post_order_blocks) { @@ -94,6 +104,9 @@ void CollectShaderInfoPass(IR::Program& program) { Visit(info, inst); } } + for (const auto& image : program.info.images) { + VisitImage(info, image); + } } } // namespace Shader::Optimization