diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 945cdb42b..d802a9d73 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -195,6 +196,41 @@ Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR } } +Id TextureColorResultType(EmitContext& ctx, const TextureDefinition& def) { + switch (def.component_type) { + case SamplerComponentType::Float: + case SamplerComponentType::Depth: + return ctx.F32[4]; + case SamplerComponentType::Sint: + return ctx.S32[4]; + case SamplerComponentType::Stencil: + return ctx.U32[4]; + case SamplerComponentType::Uint: + return ctx.U32[4]; + } + throw InvalidArgument("Invalid sampler component type {}", def.component_type); +} + +Id TextureSampleResultToFloat(EmitContext& ctx, const TextureDefinition& def, Id color) { + switch (def.component_type) { + case SamplerComponentType::Float: + case SamplerComponentType::Depth: + return color; + case SamplerComponentType::Sint: + return ctx.OpConvertSToF(ctx.F32[4], color); + case SamplerComponentType::Stencil: + { + const Id converted{ctx.OpConvertUToF(ctx.F32[4], color)}; + const Id inv255{ctx.Const(1.0f / 255.0f)}; + const Id scale{ctx.ConstantComposite(ctx.F32[4], inv255, inv255, inv255, inv255)}; + return ctx.OpFMul(ctx.F32[4], converted, scale); + } + case SamplerComponentType::Uint: + return ctx.OpConvertUToF(ctx.F32[4], color); + } + throw InvalidArgument("Invalid sampler component type {}", def.component_type); +} + Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& index) { if (!index.IsImmediate() || index.U32() != 0) { throw NotImplementedException("Indirect image indexing"); @@ -449,31 +485,39 @@ Id EmitBoundImageWrite(EmitContext&) { Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id bias_lc, const IR::Value& offset) { const auto info{inst->Flags()}; + const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; + const Id color_type{TextureColorResultType(ctx, def)}; + const Id texture{Texture(ctx, info, index)}; + Id color{}; if (ctx.stage == Stage::Fragment) { const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, bias_lc, offset); - return Emit(&EmitContext::OpImageSparseSampleImplicitLod, - &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4], - Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); + color = Emit(&EmitContext::OpImageSparseSampleImplicitLod, + &EmitContext::OpImageSampleImplicitLod, ctx, inst, color_type, texture, + coords, operands.MaskOptional(), operands.Span()); } else { // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as // if the lod was explicitly zero. This may change on Turing with implicit compute // derivatives const Id lod{ctx.Const(0.0f)}; const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset); - return Emit(&EmitContext::OpImageSparseSampleExplicitLod, - &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], - Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); + color = Emit(&EmitContext::OpImageSparseSampleExplicitLod, + &EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, texture, + coords, operands.Mask(), operands.Span()); } + return TextureSampleResultToFloat(ctx, def, color); } Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id lod, const IR::Value& offset) { const auto info{inst->Flags()}; + const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; + const Id color_type{TextureColorResultType(ctx, def)}; const ImageOperands operands(ctx, false, true, false, lod, offset); - return Emit(&EmitContext::OpImageSparseSampleExplicitLod, - &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], - Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); + const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod, + &EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, + Texture(ctx, info, index), coords, operands.Mask(), operands.Span())}; + return TextureSampleResultToFloat(ctx, def, color); } Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, @@ -509,13 +553,18 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, const IR::Value& offset, const IR::Value& offset2) { const auto info{inst->Flags()}; + const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; + const Id color_type{TextureColorResultType(ctx, def)}; const ImageOperands operands(ctx, offset, offset2); + const Id texture{Texture(ctx, info, index)}; if (ctx.profile.need_gather_subpixel_offset) { coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords); } - return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, - ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component), - operands.MaskOptional(), operands.Span()); + const Id color{ + Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, color_type, + texture, coords, ctx.Const(info.gather_component), operands.MaskOptional(), + operands.Span())}; + return TextureSampleResultToFloat(ctx, def, color); } Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, @@ -533,6 +582,9 @@ Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, Id lod, Id ms) { const auto info{inst->Flags()}; + const TextureDefinition* def = + info.type == TextureType::Buffer ? nullptr : &ctx.textures.at(info.descriptor_index); + const Id result_type{def ? TextureColorResultType(ctx, *def) : ctx.F32[4]}; AddOffsetToCoordinates(ctx, info, coords, offset); if (info.type == TextureType::Buffer) { lod = Id{}; @@ -542,8 +594,13 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c lod = Id{}; } const ImageOperands operands(lod, ms); - return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], - TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); + Id color{Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, + result_type, TextureImage(ctx, info, index), coords, operands.MaskOptional(), + operands.Span())}; + if (def) { + color = TextureSampleResultToFloat(ctx, *def, color); + } + return color; } Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, @@ -588,14 +645,17 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id derivatives, const IR::Value& offset, Id lod_clamp) { const auto info{inst->Flags()}; + const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; + const Id color_type{TextureColorResultType(ctx, def)}; const auto operands = info.num_derivatives == 3 ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, ctx.Def(offset), {}, lod_clamp) : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, info.num_derivatives, offset, lod_clamp); - return Emit(&EmitContext::OpImageSparseSampleExplicitLod, - &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], - Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); + const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod, + &EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, + Texture(ctx, info, index), coords, operands.Mask(), operands.Span())}; + return TextureSampleResultToFloat(ctx, def, color); } Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 6527eb3d8..4b94bc81d 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -26,28 +26,40 @@ enum class Operation { FPMax, }; -Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { +Id ComponentScalarType(EmitContext& ctx, SamplerComponentType component_type) { + switch (component_type) { + case SamplerComponentType::Float: + case SamplerComponentType::Depth: + return ctx.F32[1]; + case SamplerComponentType::Sint: + case SamplerComponentType::Stencil: + return ctx.S32[1]; + case SamplerComponentType::Uint: + return ctx.U32[1]; + } + throw InvalidArgument("Invalid sampler component type {}", component_type); +} + +Id ImageType(EmitContext& ctx, const TextureDescriptor& desc, Id sampled_type) { const spv::ImageFormat format{spv::ImageFormat::Unknown}; - // Use integer type for integer textures to match the actual texture format - const Id type{desc.is_integer ? ctx.U32[1] : ctx.F32[1]}; const bool depth{desc.is_depth}; const bool ms{desc.is_multisample}; switch (desc.type) { case TextureType::Color1D: - return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format); + return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, depth, false, false, 1, format); case TextureType::ColorArray1D: - return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format); + return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, depth, true, false, 1, format); case TextureType::Color2D: case TextureType::Color2DRect: - return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format); + return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, depth, false, ms, 1, format); case TextureType::ColorArray2D: - return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format); + return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, depth, true, ms, 1, format); case TextureType::Color3D: - return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format); + return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, depth, false, false, 1, format); case TextureType::ColorCube: - return ctx.TypeImage(type, spv::Dim::Cube, depth, false, false, 1, format); + return ctx.TypeImage(sampled_type, spv::Dim::Cube, depth, false, false, 1, format); case TextureType::ColorArrayCube: - return ctx.TypeImage(type, spv::Dim::Cube, depth, true, false, 1, format); + return ctx.TypeImage(sampled_type, spv::Dim::Cube, depth, true, false, 1, format); case TextureType::Buffer: break; } @@ -326,6 +338,9 @@ void DefineSsbos(EmitContext& ctx, StorageTypeDefinition& type_def, ctx.Decorate(id, spv::Decoration::Binding, binding); ctx.Decorate(id, spv::Decoration::DescriptorSet, 0U); ctx.Name(id, fmt::format("ssbo{}", index)); + if (!desc.is_written) { + ctx.Decorate(id, spv::Decoration::NonWritable); + } if (ctx.profile.supported_spirv >= 0x00010400) { ctx.interfaces.push_back(id); } @@ -557,6 +572,7 @@ void EmitContext::DefineCommonTypes(const Info& info) { output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32"); output_u32 = Name(TypePointer(spv::StorageClass::Output, U32[1]), "output_u32"); + output_s32 = Name(TypePointer(spv::StorageClass::Output, S32[1]), "output_s32"); if (info.uses_int8 && profile.support_int8) { AddCapability(spv::Capability::Int8); @@ -1370,7 +1386,8 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) { void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_index) { textures.reserve(info.texture_descriptors.size()); for (const TextureDescriptor& desc : info.texture_descriptors) { - const Id image_type{ImageType(*this, desc)}; + const Id result_type{ComponentScalarType(*this, desc.component_type)}; + const Id image_type{ImageType(*this, desc, result_type)}; const Id sampled_type{TypeSampledImage(image_type)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)}; const Id desc_type{DescType(*this, sampled_type, pointer_type, desc.count)}; @@ -1383,9 +1400,10 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in .sampled_type = sampled_type, .pointer_type = pointer_type, .image_type = image_type, + .result_type = result_type, .count = desc.count, .is_multisample = desc.is_multisample, - .is_integer = desc.is_integer, + .component_type = desc.component_type, }); if (profile.supported_spirv >= 0x00010400) { interfaces.push_back(id); @@ -1444,6 +1462,9 @@ void EmitContext::DefineInputs(const IR::Program& program) { } if (info.uses_sample_id) { sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId); + if (stage == Stage::Fragment) { + Decorate(sample_id, spv::Decoration::Flat); + } } if (info.uses_is_helper_invocation) { is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation); @@ -1454,6 +1475,13 @@ void EmitContext::DefineInputs(const IR::Program& program) { subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR); subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR); subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR); + if (stage == Stage::Fragment) { + Decorate(subgroup_mask_eq, spv::Decoration::Flat); + Decorate(subgroup_mask_lt, spv::Decoration::Flat); + Decorate(subgroup_mask_le, spv::Decoration::Flat); + Decorate(subgroup_mask_gt, spv::Decoration::Flat); + Decorate(subgroup_mask_ge, spv::Decoration::Flat); + } } if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || (profile.warp_size_potentially_larger_than_guest && @@ -1461,7 +1489,9 @@ void EmitContext::DefineInputs(const IR::Program& program) { AddCapability(spv::Capability::GroupNonUniform); subgroup_local_invocation_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); - Decorate(subgroup_local_invocation_id, spv::Decoration::Flat); + if (stage == Stage::Fragment) { + Decorate(subgroup_local_invocation_id, spv::Decoration::Flat); + } } if (info.uses_fswzadd) { const Id f32_one{Const(1.0f)}; @@ -1473,6 +1503,9 @@ void EmitContext::DefineInputs(const IR::Program& program) { } if (loads[IR::Attribute::PrimitiveId]) { primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::PrimitiveId); + if (stage == Stage::Fragment) { + Decorate(primitive_id, spv::Decoration::Flat); + } } if (loads[IR::Attribute::Layer]) { AddCapability(spv::Capability::Geometry); @@ -1564,17 +1597,21 @@ void EmitContext::DefineInputs(const IR::Program& program) { if (stage != Stage::Fragment) { continue; } - switch (info.interpolation[index]) { - case Interpolation::Smooth: - // Default - // Decorate(id, spv::Decoration::Smooth); - break; - case Interpolation::NoPerspective: - Decorate(id, spv::Decoration::NoPerspective); - break; - case Interpolation::Flat: + const bool is_integer = input_type == AttributeType::SignedInt || + input_type == AttributeType::UnsignedInt; + if (is_integer) { Decorate(id, spv::Decoration::Flat); - break; + } else { + switch (info.interpolation[index]) { + case Interpolation::Smooth: + break; + case Interpolation::NoPerspective: + Decorate(id, spv::Decoration::NoPerspective); + break; + case Interpolation::Flat: + Decorate(id, spv::Decoration::Flat); + break; + } } } if (stage == Stage::TessellationEval) { diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h index a17821009..126e3d1d0 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -35,9 +35,10 @@ struct TextureDefinition { Id sampled_type; Id pointer_type; Id image_type; + Id result_type; u32 count; bool is_multisample; - bool is_integer; + SamplerComponentType component_type; }; struct TextureBufferDefinition { @@ -244,6 +245,7 @@ public: Id output_f32{}; Id output_u32{}; + Id output_s32{}; Id image_buffer_type{}; Id image_u32{};