From eb04a096dee3b5eadbd587247653f0257df4309f Mon Sep 17 00:00:00 2001 From: Zephyron Date: Fri, 2 Jan 2026 17:50:00 +1000 Subject: [PATCH 1/6] feat(spirv): implement texture component type handling in SPIR-V backend Update SPIR-V backend to use component-specific sampled types for textures, ensuring correct type conversions for integer and depth/stencil textures. - Add ComponentScalarType helper function - Update ImageType to accept component-specific sampled types - Add TextureColorResultType and TextureSampleResultToFloat helpers - Update all texture sampling functions to handle component types - Add output_s32 for signed integer outputs - Add Flat decorations for integer inputs in fragment shaders - Add NonWritable decoration for read-only storage buffers Co-Authored-By: ForrestMarkX Signed-off-by: Zephyron --- .../backend/spirv/emit_spirv_image.cpp | 94 +++++++++++++++---- .../backend/spirv/spirv_emit_context.cpp | 92 +++++++++++++----- .../backend/spirv/spirv_emit_context.h | 7 +- 3 files changed, 150 insertions(+), 43 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 945cdb42b..0364f7f18 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 2026 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 } } +/// Get the result type for texture color sampling based on component type +[[nodiscard]] 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: + case SamplerComponentType::Uint: + return ctx.U32[4]; + } + throw InvalidArgument("Invalid sampler component type {}", def.component_type); +} + +/// Convert texture sample result to float, handling integer and stencil types +[[nodiscard]] 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 13f6a97d2..13d0361c8 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -26,27 +26,42 @@ enum class Operation { FPMax, }; -Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { +/// Get the scalar type ID for a given sampler component type +[[nodiscard]] 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}; - const Id type{ctx.F32[1]}; +>>>>>>> f8de99641 (feat(spirv): implement texture component type handling in SPIR-V backend) 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; } @@ -275,7 +290,7 @@ void Name(EmitContext& ctx, Id object, std::string_view format_str, Args&&... ar .c_str()); } -void DefineConstBuffers(EmitContext& ctx, const Info& info, Id UniformDefinitions::*member_type, +void DefineConstBuffers(EmitContext& ctx, const Info& info, Id UniformDefinitions::* member_type, u32 binding, Id type, char type_char, u32 element_size) { const Id array_type{ctx.TypeArray(type, ctx.Const(65536U / element_size))}; ctx.Decorate(array_type, spv::Decoration::ArrayStride, element_size); @@ -306,7 +321,7 @@ void DefineConstBuffers(EmitContext& ctx, const Info& info, Id UniformDefinition } void DefineSsbos(EmitContext& ctx, StorageTypeDefinition& type_def, - Id StorageDefinitions::*member_type, const Info& info, u32 binding, Id type, + Id StorageDefinitions::* member_type, const Info& info, u32 binding, Id type, u32 stride) { const Id array_type{ctx.TypeRuntimeArray(type)}; ctx.Decorate(array_type, spv::Decoration::ArrayStride, stride); @@ -325,6 +340,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); } @@ -556,6 +574,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); @@ -1169,7 +1188,7 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) { if (!info.uses_cbuf_indirect) { return; } - const auto make_accessor{[&](Id buffer_type, Id UniformDefinitions::*member_ptr) { + const auto make_accessor{[&](Id buffer_type, Id UniformDefinitions::* member_ptr) { const Id func_type{TypeFunction(buffer_type, U32[1], U32[1])}; const Id func{OpFunction(buffer_type, spv::FunctionControlMask::MaskNone, func_type)}; const Id binding{OpFunctionParameter(U32[1])}; @@ -1369,7 +1388,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)}; @@ -1382,8 +1402,11 @@ 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, + .component_type = desc.component_type, +>>>>>>> f8de99641 (feat(spirv): implement texture component type handling in SPIR-V backend) }); if (profile.supported_spirv >= 0x00010400) { interfaces.push_back(id); @@ -1442,6 +1465,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); @@ -1452,6 +1478,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 && @@ -1459,7 +1492,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)}; @@ -1471,6 +1506,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); @@ -1562,17 +1600,23 @@ 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: + // Default + // Decorate(id, spv::Decoration::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 40adcb6b6..b7c6e7838 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -34,8 +34,10 @@ struct TextureDefinition { Id sampled_type; Id pointer_type; Id image_type; + Id result_type; u32 count; bool is_multisample; + SamplerComponentType component_type; }; struct TextureBufferDefinition { @@ -67,7 +69,7 @@ struct UniformDefinitions { Id U32x2{}; Id U32x4{}; - constexpr static size_t NumElements(Id UniformDefinitions::*member_ptr) { + constexpr static size_t NumElements(Id UniformDefinitions::* member_ptr) { if (member_ptr == &UniformDefinitions::U8) { return 1; } @@ -96,7 +98,7 @@ struct UniformDefinitions { return 1; } - constexpr static bool IsFloat(Id UniformDefinitions::*member_ptr) { + constexpr static bool IsFloat(Id UniformDefinitions::* member_ptr) { if (member_ptr == &UniformDefinitions::F32) { return true; } @@ -242,6 +244,7 @@ public: Id output_f32{}; Id output_u32{}; + Id output_s32{}; Id image_buffer_type{}; Id image_u32{}; From 46de959188e0e2d33e18e748e09f7414b32b01db Mon Sep 17 00:00:00 2001 From: Zephyron Date: Fri, 2 Jan 2026 17:51:22 +1000 Subject: [PATCH 2/6] feat(shader): add SamplerComponentType enum and infrastructure Add SamplerComponentType enum to classify texture component types (float, signed int, unsigned int, depth, stencil) and add ReadTextureComponentType method to Environment interface. - Add SamplerComponentType enum with constexpr helper functions - Add component_type field to TextureDescriptor struct - Add ReadTextureComponentType virtual method to Environment base class Co-Authored-By: ForrestMarkX Signed-off-by: Zephyron --- src/shader_recompiler/environment.h | 6 ++++++ src/shader_recompiler/shader_info.h | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h index 5dbbc7e61..217f0d404 100644 --- a/src/shader_recompiler/environment.h +++ b/src/shader_recompiler/environment.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -22,6 +23,11 @@ public: [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0; + /// Read the component type of a texture from its raw handle + /// @param raw_handle The raw texture handle to query + /// @return The component type of the texture (float, signed int, unsigned int, depth, or stencil) + [[nodiscard]] virtual SamplerComponentType ReadTextureComponentType(u32 raw_handle) = 0; + [[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0; [[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0; diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index 858e3eb84..3a473710b 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -158,6 +158,25 @@ enum class ImageFormat : u32 { R32G32B32A32_SFLOAT, }; +/// Texture sampler component type classification +enum class SamplerComponentType : u8 { + Float, ///< Floating-point texture components + Sint, ///< Signed integer texture components + Uint, ///< Unsigned integer texture components + Depth, ///< Depth texture components + Stencil, ///< Stencil texture components +}; + +/// Check if a component type represents an integer type +[[nodiscard]] constexpr bool IsInteger(SamplerComponentType type) noexcept { + return type == SamplerComponentType::Sint || type == SamplerComponentType::Uint; +} + +/// Check if a component type represents a depth or stencil type +[[nodiscard]] constexpr bool IsDepthStencil(SamplerComponentType type) noexcept { + return type == SamplerComponentType::Depth || type == SamplerComponentType::Stencil; +} + enum class Interpolation { Smooth, Flat, @@ -211,6 +230,7 @@ using ImageBufferDescriptors = boost::container::small_vector Date: Fri, 2 Jan 2026 17:51:38 +1000 Subject: [PATCH 3/6] feat(video-core): add texture component type caching and serialization Implement texture component type conversion and caching in shader environments, with full serialization support for shader cache. - Add ConvertSamplerComponentType using std::bitset for component tracking - Implement ReadTextureComponentType for Graphics/Compute/File environments - Add texture_component_types cache to all environment classes - Update Serialize/Deserialize to include component types - Cache component types alongside texture types for performance Co-Authored-By: ForrestMarkX Signed-off-by: Zephyron --- src/video_core/shader_environment.cpp | 116 ++++++++++++++++++++++++++ src/video_core/shader_environment.h | 9 ++ 2 files changed, 125 insertions(+) diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 18ae76420..d325f26f9 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include +#include #include #include #include @@ -69,6 +70,58 @@ static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture entry.a_type, entry.srgb_conversion)); } +static Shader::SamplerComponentType ConvertSamplerComponentType( + const Tegra::Texture::TICEntry& entry) { + const auto pixel_format = PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, + entry.b_type, entry.a_type, + entry.srgb_conversion); + const auto surface_type = VideoCore::Surface::GetFormatType(pixel_format); + if (entry.depth_texture != 0 || surface_type == VideoCore::Surface::SurfaceType::Depth) { + return Shader::SamplerComponentType::Depth; + } + if (surface_type == VideoCore::Surface::SurfaceType::Stencil) { + return Shader::SamplerComponentType::Stencil; + } + if (surface_type == VideoCore::Surface::SurfaceType::DepthStencil) { + return entry.depth_texture != 0 ? Shader::SamplerComponentType::Depth + : Shader::SamplerComponentType::Stencil; + } + + // Use std::bitset for cleaner tracking of component types + std::bitset<2> component_flags{}; // [0]=has_signed, [1]=has_unsigned + const auto accumulate = [&component_flags](const Tegra::Texture::ComponentType component) { + switch (component) { + case Tegra::Texture::ComponentType::SINT: + component_flags.set(0); + break; + case Tegra::Texture::ComponentType::UINT: + component_flags.set(1); + break; + default: + break; + } + }; + + accumulate(entry.r_type); + accumulate(entry.g_type); + accumulate(entry.b_type); + accumulate(entry.a_type); + + if (component_flags[0] && !component_flags[1]) { + return Shader::SamplerComponentType::Sint; + } + if (component_flags[1] && !component_flags[0]) { + return Shader::SamplerComponentType::Uint; + } + if (component_flags[0]) { + return Shader::SamplerComponentType::Sint; + } + if (component_flags[1]) { + return Shader::SamplerComponentType::Uint; + } + return Shader::SamplerComponentType::Float; +} + static std::string_view StageToPrefix(Shader::Stage stage) { switch (stage) { case Shader::Stage::VertexB: @@ -199,6 +252,7 @@ void GenericEnvironment::Serialize(std::ofstream& file) const { const u64 code_size{static_cast(CachedSizeBytes())}; const u64 num_texture_types{static_cast(texture_types.size())}; const u64 num_texture_pixel_formats{static_cast(texture_pixel_formats.size())}; + const u64 num_texture_component_types{static_cast(texture_component_types.size())}; const u64 num_cbuf_values{static_cast(cbuf_values.size())}; const u64 num_cbuf_replacement_values{static_cast(cbuf_replacements.size())}; @@ -206,6 +260,8 @@ void GenericEnvironment::Serialize(std::ofstream& file) const { .write(reinterpret_cast(&num_texture_types), sizeof(num_texture_types)) .write(reinterpret_cast(&num_texture_pixel_formats), sizeof(num_texture_pixel_formats)) + .write(reinterpret_cast(&num_texture_component_types), + sizeof(num_texture_component_types)) .write(reinterpret_cast(&num_cbuf_values), sizeof(num_cbuf_values)) .write(reinterpret_cast(&num_cbuf_replacement_values), sizeof(num_cbuf_replacement_values)) @@ -222,6 +278,10 @@ void GenericEnvironment::Serialize(std::ofstream& file) const { file.write(reinterpret_cast(&key), sizeof(key)) .write(reinterpret_cast(&type), sizeof(type)); } + for (const auto& [key, component] : texture_component_types) { + file.write(reinterpret_cast(&key), sizeof(key)) + .write(reinterpret_cast(&component), sizeof(component)); + } for (const auto& [key, format] : texture_pixel_formats) { file.write(reinterpret_cast(&key), sizeof(key)) .write(reinterpret_cast(&format), sizeof(format)); @@ -406,12 +466,31 @@ std::optional GraphicsEnvironment::GetReplaceConstBuffe } Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { + const auto it{texture_types.find(handle)}; + if (it != texture_types.end()) { + return it->second; + } const auto& regs{maxwell3d->regs}; const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; auto entry = ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle); const Shader::TextureType result{ConvertTextureType(entry)}; texture_types.emplace(handle, result); + texture_component_types.emplace(handle, ConvertSamplerComponentType(entry)); + return result; +} + +Shader::SamplerComponentType GraphicsEnvironment::ReadTextureComponentType(u32 handle) { + const auto it{texture_component_types.find(handle)}; + if (it != texture_component_types.end()) { + return it->second; + } + const auto& regs{maxwell3d->regs}; + const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; + auto entry = + ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle); + const Shader::SamplerComponentType result{ConvertSamplerComponentType(entry)}; + texture_component_types.emplace(handle, result); return result; } @@ -463,11 +542,29 @@ u32 ComputeEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { } Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) { + const auto it{texture_types.find(handle)}; + if (it != texture_types.end()) { + return it->second; + } const auto& regs{kepler_compute->regs}; const auto& qmd{kepler_compute->launch_description}; auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); const Shader::TextureType result{ConvertTextureType(entry)}; texture_types.emplace(handle, result); + texture_component_types.emplace(handle, ConvertSamplerComponentType(entry)); + return result; +} + +Shader::SamplerComponentType ComputeEnvironment::ReadTextureComponentType(u32 handle) { + const auto it{texture_component_types.find(handle)}; + if (it != texture_component_types.end()) { + return it->second; + } + const auto& regs{kepler_compute->regs}; + const auto& qmd{kepler_compute->launch_description}; + auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); + const Shader::SamplerComponentType result{ConvertSamplerComponentType(entry)}; + texture_component_types.emplace(handle, result); return result; } @@ -493,12 +590,15 @@ void FileEnvironment::Deserialize(std::ifstream& file) { u64 code_size{}; u64 num_texture_types{}; u64 num_texture_pixel_formats{}; + u64 num_texture_component_types{}; u64 num_cbuf_values{}; u64 num_cbuf_replacement_values{}; file.read(reinterpret_cast(&code_size), sizeof(code_size)) .read(reinterpret_cast(&num_texture_types), sizeof(num_texture_types)) .read(reinterpret_cast(&num_texture_pixel_formats), sizeof(num_texture_pixel_formats)) + .read(reinterpret_cast(&num_texture_component_types), + sizeof(num_texture_component_types)) .read(reinterpret_cast(&num_cbuf_values), sizeof(num_cbuf_values)) .read(reinterpret_cast(&num_cbuf_replacement_values), sizeof(num_cbuf_replacement_values)) @@ -518,6 +618,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) { .read(reinterpret_cast(&type), sizeof(type)); texture_types.emplace(key, type); } + for (size_t i = 0; i < num_texture_component_types; ++i) { + u32 key; + Shader::SamplerComponentType component; + file.read(reinterpret_cast(&key), sizeof(key)) + .read(reinterpret_cast(&component), sizeof(component)); + texture_component_types.emplace(key, component); + } for (size_t i = 0; i < num_texture_pixel_formats; ++i) { u32 key; Shader::TexturePixelFormat format; @@ -580,6 +687,15 @@ Shader::TextureType FileEnvironment::ReadTextureType(u32 handle) { return it->second; } +Shader::SamplerComponentType FileEnvironment::ReadTextureComponentType(u32 handle) { + const auto it{texture_component_types.find(handle)}; + if (it == texture_component_types.end()) { + LOG_WARNING(Render_Vulkan, "Texture component descriptor {:08x} not found", handle); + return Shader::SamplerComponentType::Float; + } + return it->second; +} + Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) { const auto it{texture_pixel_formats.find(handle)}; if (it == texture_pixel_formats.end()) { diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 6b372e336..9c276b2c8 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -77,6 +78,7 @@ protected: std::vector code; std::unordered_map texture_types; + std::unordered_map texture_component_types; std::unordered_map texture_pixel_formats; std::unordered_map cbuf_values; std::unordered_map cbuf_replacements; @@ -113,6 +115,8 @@ public: Shader::TextureType ReadTextureType(u32 handle) override; + Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override; + Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; bool IsTexturePixelFormatInteger(u32 handle) override; @@ -139,6 +143,8 @@ public: Shader::TextureType ReadTextureType(u32 handle) override; + Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override; + Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; bool IsTexturePixelFormatInteger(u32 handle) override; @@ -173,6 +179,8 @@ public: [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override; + [[nodiscard]] Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override; + [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; [[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override; @@ -199,6 +207,7 @@ public: private: std::vector code; std::unordered_map texture_types; + std::unordered_map texture_component_types; std::unordered_map texture_pixel_formats; std::unordered_map cbuf_values; std::unordered_map cbuf_replacements; From f95faaade599bdce0c2b8c0c26eb95190dc78451 Mon Sep 17 00:00:00 2001 From: Zephyron Date: Fri, 2 Jan 2026 17:52:07 +1000 Subject: [PATCH 4/6] feat(shader): integrate component type into texture pass optimization Update texture descriptor comparison to include component_type, ensuring textures with different component types are properly differentiated. - Add ReadTextureComponentType helper function - Include component_type in texture descriptor comparison - Use component type when creating texture descriptors Co-Authored-By: ForrestMarkX Signed-off-by: Zephyron --- src/shader_recompiler/ir_opt/texture_pass.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 242c13e09..ba87e2fcb 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -392,6 +392,11 @@ bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf)); } +[[nodiscard]] SamplerComponentType ReadTextureComponentType(Environment& env, + const ConstBufferAddr& cbuf) { + return env.ReadTextureComponentType(GetTextureHandle(env, cbuf)); +} + class Descriptors { public: explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_, @@ -429,7 +434,9 @@ public: u32 Add(const TextureDescriptor& desc) { const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) { - return desc.type == existing.type && desc.is_depth == existing.is_depth && + return desc.type == existing.type && + desc.component_type == existing.component_type && + desc.is_depth == existing.is_depth && desc.has_secondary == existing.has_secondary && desc.cbuf_index == existing.cbuf_index && desc.cbuf_offset == existing.cbuf_offset && @@ -666,6 +673,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo } else { index = descriptors.Add(TextureDescriptor{ .type = flags.type, + .component_type = ReadTextureComponentType(env, cbuf), .is_depth = flags.is_depth != 0, .is_multisample = is_multisample, .has_secondary = cbuf.has_secondary, From 9b7e7b26aad94cd541373cb7a97327cf34575004 Mon Sep 17 00:00:00 2001 From: Zephyron Date: Fri, 2 Jan 2026 17:52:35 +1000 Subject: [PATCH 5/6] feat(renderer): add BCn unswizzle compute shader Add block_linear_unswizzle_3d_bcn.comp compute shader for improved BCn format support. The shader uses GLSL 460 and handles block-space coordinate conversion for BCn compressed textures. - Add block_linear_unswizzle_3d_bcn.comp shader - Update host shaders CMakeLists to include new shader Co-Authored-By: ForrestMarkX Signed-off-by: Zephyron --- src/video_core/host_shaders/CMakeLists.txt | 1 + .../block_linear_unswizzle_3d_bcn.comp | 161 ++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index 0acee234a..a24cc5ecc 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -19,6 +19,7 @@ set(SHADER_FILES blit_color_float.frag block_linear_unswizzle_2d.comp block_linear_unswizzle_3d.comp + block_linear_unswizzle_3d_bcn.comp convert_abgr8_to_d24s8.frag convert_abgr8_to_d32f.frag convert_d32f_to_abgr8.frag diff --git a/src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp b/src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp new file mode 100644 index 000000000..a73fc31db --- /dev/null +++ b/src/video_core/host_shaders/block_linear_unswizzle_3d_bcn.comp @@ -0,0 +1,161 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 460 + +#ifdef VULKAN + #extension GL_EXT_shader_16bit_storage : require + #extension GL_EXT_shader_8bit_storage : require + #define HAS_EXTENDED_TYPES 1 + #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { + #define END_PUSH_CONSTANTS }; + #define UNIFORM(n) + #define BINDING_SWIZZLE_BUFFER 0 + #define BINDING_INPUT_BUFFER 1 + #define BINDING_OUTPUT_BUFFER 2 +#else + #extension GL_NV_gpu_shader5 : enable + #ifdef GL_NV_gpu_shader5 + #define HAS_EXTENDED_TYPES 1 + #else + #define HAS_EXTENDED_TYPES 0 + #endif + #define BEGIN_PUSH_CONSTANTS + #define END_PUSH_CONSTANTS + #define UNIFORM(n) layout(location = n) uniform + #define BINDING_SWIZZLE_BUFFER 0 + #define BINDING_INPUT_BUFFER 1 + #define BINDING_OUTPUT_BUFFER 0 +#endif + +// --- Push Constants / Uniforms --- +#ifdef VULKAN +layout(push_constant) uniform PushConstants { + uvec3 blocks_dim; // Offset 0 + uint bytes_per_block_log2; // Offset 12 + + uvec3 origin; // Offset 16 + uint slice_size; // Offset 28 + + uint block_size; // Offset 32 + uint x_shift; // Offset 36 + uint block_height; // Offset 40 + uint block_height_mask; // Offset 44 + + uint block_depth; // Offset 48 + uint block_depth_mask; // Offset 52 + int _pad; // Offset 56 + + ivec3 destination; // Offset 60 +} pc; +#else +BEGIN_PUSH_CONSTANTS + UNIFORM(0) uvec3 origin; + UNIFORM(1) ivec3 destination; + UNIFORM(2) uint bytes_per_block_log2; + UNIFORM(3) uint slice_size; + UNIFORM(4) uint block_size; + UNIFORM(5) uint x_shift; + UNIFORM(6) uint block_height; + UNIFORM(7) uint block_height_mask; + UNIFORM(8) uint block_depth; + UNIFORM(9) uint block_depth_mask; + UNIFORM(10) uvec3 blocks_dim; +END_PUSH_CONSTANTS +#define pc // Map pc prefix to nothing for OpenGL compatibility +#endif + +// --- Buffers --- +layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable { + uint swizzle_table[]; +}; + +#if HAS_EXTENDED_TYPES + layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; }; + layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; }; +#endif +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; }; +layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU128 { uvec4 u128data[]; }; + +layout(binding = BINDING_OUTPUT_BUFFER, std430) buffer OutputBuffer { + uint out_u32[]; +}; + +// --- Constants --- +layout(local_size_x = 32, local_size_y = 8, local_size_z = 1) in; + +const uint GOB_SIZE_X = 64; +const uint GOB_SIZE_Y = 8; +const uint GOB_SIZE_Z = 1; +const uint GOB_SIZE = GOB_SIZE_X * GOB_SIZE_Y * GOB_SIZE_Z; + +const uint GOB_SIZE_X_SHIFT = 6; +const uint GOB_SIZE_Y_SHIFT = 3; +const uint GOB_SIZE_Z_SHIFT = 0; +const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT; +const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u); + +// --- Helpers --- +uint SwizzleOffset(uvec2 pos) { + pos &= SWIZZLE_MASK; + return swizzle_table[pos.y * 64u + pos.x]; +} + +uvec4 ReadTexel(uint offset) { + uint bpl2 = pc.bytes_per_block_log2; + switch (bpl2) { +#if HAS_EXTENDED_TYPES + case 0u: return uvec4(u8data[offset], 0u, 0u, 0u); + case 1u: return uvec4(u16data[offset / 2u], 0u, 0u, 0u); +#else + case 0u: return uvec4(bitfieldExtract(u32data[offset / 4u], int((offset * 8u) & 24u), 8), 0u, 0u, 0u); + case 1u: return uvec4(bitfieldExtract(u32data[offset / 4u], int((offset * 8u) & 16u), 16), 0u, 0u, 0u); +#endif + case 2u: return uvec4(u32data[offset / 4u], 0u, 0u, 0u); + case 3u: return uvec4(u64data[offset / 8u], 0u, 0u); + case 4u: return u128data[offset / 16u]; + } + return uvec4(0u); +} + +void main() { + uvec3 block_coord = gl_GlobalInvocationID; + if (any(greaterThanEqual(block_coord, pc.blocks_dim))) { + return; + } + + uint bytes_per_block = 1u << pc.bytes_per_block_log2; + // Origin is in pixels, divide by 4 for block-space (e.g. BCn formats) + uvec3 pos; + pos.x = (block_coord.x + (pc.origin.x >> 2u)) * bytes_per_block; + pos.y = block_coord.y + (pc.origin.y >> 2u); + pos.z = block_coord.z + pc.origin.z; + + uint swizzle = SwizzleOffset(pos.xy); + uint block_y = pos.y >> GOB_SIZE_Y_SHIFT; + uint offset = 0u; + // Apply block-linear offsets + offset += (pos.z >> pc.block_depth) * pc.slice_size; + offset += (pos.z & pc.block_depth_mask) << (GOB_SIZE_SHIFT + pc.block_height); + offset += (block_y >> pc.block_height) * pc.block_size; + offset += (block_y & pc.block_height_mask) << GOB_SIZE_SHIFT; + offset += (pos.x >> GOB_SIZE_X_SHIFT) << pc.x_shift; + offset += swizzle; + + uvec4 texel = ReadTexel(offset); + + // Calculate linear output index + uint block_index = block_coord.x + + (block_coord.y * pc.blocks_dim.x) + + (block_coord.z * pc.blocks_dim.x * pc.blocks_dim.y); + uint out_idx = block_index * (bytes_per_block >> 2u); + + out_u32[out_idx] = texel.x; + out_u32[out_idx + 1] = texel.y; + if (pc.bytes_per_block_log2 == 4u) { + out_u32[out_idx + 2] = texel.z; + out_u32[out_idx + 3] = texel.w; + } +} From 9a8e6a20cae89694eaf3d9442b1ccfa9a2ba2395 Mon Sep 17 00:00:00 2001 From: collecting Date: Tue, 10 Feb 2026 12:06:54 -0500 Subject: [PATCH 6/6] fix(texture): Apply F32 Adjustments for Proper Z-A Rendering --- .../backend/spirv/emit_spirv_image.cpp | 28 ++++++++----------- .../backend/spirv/spirv_emit_context.cpp | 6 ++-- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 0364f7f18..feb335f07 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -201,10 +201,10 @@ Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR switch (def.component_type) { case SamplerComponentType::Float: case SamplerComponentType::Depth: + case SamplerComponentType::Stencil: return ctx.F32[4]; case SamplerComponentType::Sint: return ctx.S32[4]; - case SamplerComponentType::Stencil: case SamplerComponentType::Uint: return ctx.U32[4]; } @@ -212,19 +212,16 @@ Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR } /// Convert texture sample result to float, handling integer and stencil types -[[nodiscard]] Id TextureSampleResultToFloat(EmitContext& ctx, const TextureDefinition& def, Id color) { +[[nodiscard]] Id TextureSampleResultToFloat(EmitContext& ctx, const TextureDefinition& def, + Id color) { switch (def.component_type) { case SamplerComponentType::Float: case SamplerComponentType::Depth: + case SamplerComponentType::Stencil: 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); } @@ -493,8 +490,8 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, bias_lc, offset); color = Emit(&EmitContext::OpImageSparseSampleImplicitLod, - &EmitContext::OpImageSampleImplicitLod, ctx, inst, color_type, texture, - coords, operands.MaskOptional(), operands.Span()); + &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 @@ -502,8 +499,8 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& const Id lod{ctx.Const(0.0f)}; const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset); color = Emit(&EmitContext::OpImageSparseSampleExplicitLod, - &EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, texture, - coords, operands.Mask(), operands.Span()); + &EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, texture, coords, + operands.Mask(), operands.Span()); } return TextureSampleResultToFloat(ctx, def, color); } @@ -560,10 +557,9 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id if (ctx.profile.need_gather_subpixel_offset) { coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords); } - const Id color{ - Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, color_type, - texture, 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); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 13d0361c8..5a0d43394 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -33,17 +33,18 @@ enum class Operation { case SamplerComponentType::Depth: return ctx.F32[1]; case SamplerComponentType::Sint: - case SamplerComponentType::Stencil: return ctx.S32[1]; case SamplerComponentType::Uint: return ctx.U32[1]; + case SamplerComponentType::Stencil: + return ctx.F32[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}; ->>>>>>> f8de99641 (feat(spirv): implement texture component type handling in SPIR-V backend) + const bool depth{desc.is_depth}; const bool ms{desc.is_multisample}; switch (desc.type) { @@ -1406,7 +1407,6 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in .count = desc.count, .is_multisample = desc.is_multisample, .component_type = desc.component_type, ->>>>>>> f8de99641 (feat(spirv): implement texture component type handling in SPIR-V backend) }); if (profile.supported_spirv >= 0x00010400) { interfaces.push_back(id);