diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index df4da81356..0b3a1a7c08 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -260,6 +260,13 @@ bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) { return ctx.textures.at(info.descriptor_index).is_multisample; } +bool IsTextureInteger(EmitContext& ctx, const IR::TextureInstInfo& info) { + if (info.type == TextureType::Buffer) { + return false; + } + return ctx.textures.at(info.descriptor_index).is_integer; +} + Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { const auto info{inst->Flags()}; if (info.relaxed_precision != 0) { @@ -480,11 +487,14 @@ 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 bool is_integer{IsTextureInteger(ctx, info)}; + const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]}; + 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], + color = Emit(&EmitContext::OpImageSparseSampleImplicitLod, + &EmitContext::OpImageSampleImplicitLod, ctx, inst, result_type, Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); } else { // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as @@ -492,26 +502,29 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& // 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], + color = Emit(&EmitContext::OpImageSparseSampleExplicitLod, + &EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type, Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); } + return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : 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 bool is_integer{IsTextureInteger(ctx, info)}; + const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]}; const ImageOperands operands(ctx, false, true, false, lod, offset); Id result = Emit(&EmitContext::OpImageSparseSampleExplicitLod, - &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], + &EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type, Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); #ifdef __ANDROID__ - if (Settings::values.fix_bloom_effects.GetValue()) { + if (!is_integer && Settings::values.fix_bloom_effects.GetValue()) { result = ctx.OpVectorTimesScalar(ctx.F32[4], result, ctx.Const(0.98f)); } #endif - return result; + return is_integer ? ctx.OpBitcast(ctx.F32[4], result) : result; } Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, @@ -547,30 +560,39 @@ 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 bool is_integer{IsTextureInteger(ctx, info)}; + const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]}; const ImageOperands operands(ctx, offset, offset2); 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, + result_type, Texture(ctx, info, index), coords, + ctx.Const(info.gather_component), operands.MaskOptional(), + operands.Span())}; + return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color; } Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, const IR::Value& offset, const IR::Value& offset2, Id dref) { const auto info{inst->Flags()}; + const bool is_integer{IsTextureInteger(ctx, info)}; + const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]}; const ImageOperands operands(ctx, offset, offset2); if (ctx.profile.need_gather_subpixel_offset) { coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords); } - return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst, - ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(), - operands.Span()); + const Id color{Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, + ctx, inst, result_type, Texture(ctx, info, index), coords, dref, + operands.MaskOptional(), operands.Span())}; + return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color; } 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 bool is_integer{IsTextureInteger(ctx, info)}; + const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]}; AddOffsetToCoordinates(ctx, info, coords, offset); if (info.type == TextureType::Buffer) { lod = Id{}; @@ -580,8 +602,10 @@ 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()); + const Id color{Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, + result_type, TextureImage(ctx, info, index), coords, + operands.MaskOptional(), operands.Span())}; + return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color; } Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, @@ -626,14 +650,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 bool is_integer{IsTextureInteger(ctx, info)}; + const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]}; 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, result_type, + Texture(ctx, info, index), coords, operands.Mask(), operands.Span())}; + return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : 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 b97e0b55f6..c5c6b2655a 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -30,7 +30,7 @@ enum class Operation { Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { const spv::ImageFormat format{spv::ImageFormat::Unknown}; - const Id type{ctx.F32[1]}; + 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) { @@ -1375,6 +1375,7 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in .image_type = image_type, .count = desc.count, .is_multisample = desc.is_multisample, + .is_integer = desc.is_integer, }); if (profile.supported_spirv >= 0x00010400) { 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 55385de0b0..504bffc0ed 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -42,6 +42,7 @@ struct TextureDefinition { Id image_type; u32 count; bool is_multisample; + bool is_integer; }; struct TextureBufferDefinition { diff --git a/src/shader_recompiler/frontend/ir/modifiers.h b/src/shader_recompiler/frontend/ir/modifiers.h index c20c2401f7..991c51b1d6 100644 --- a/src/shader_recompiler/frontend/ir/modifiers.h +++ b/src/shader_recompiler/frontend/ir/modifiers.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -43,6 +46,10 @@ union TextureInstInfo { BitField<25, 2, u32> num_derivatives; BitField<27, 3, ImageFormat> image_format; BitField<30, 1, u32> ndv_is_active; + /// Only meaningful for the sampled-texture family (ImageSampleImplicitLod, ImageFetch, + /// ImageGather, etc.); unused/zero for storage-image opcodes, which carry their own + /// is_integer via ImageDescriptor/ImageBufferDescriptor instead. + BitField<31, 1, u32> is_integer; }; static_assert(sizeof(TextureInstInfo) <= sizeof(u32)); diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index b5ad274292..b97f6dbf17 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -562,6 +562,7 @@ public: })}; // TODO: Read this from TIC texture_descriptors[index].is_multisample |= desc.is_multisample; + texture_descriptors[index].is_integer |= desc.is_integer; return index; } @@ -791,10 +792,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo }); } else { count = std::min(count, sampled_dynamic_cap); + const bool is_integer{IsTexturePixelFormatIntegerCached(env, cbuf)}; index = descriptors.Add(TextureDescriptor{ .type = flags.type, .is_depth = flags.is_depth != 0, .is_multisample = is_multisample, + .is_integer = is_integer, .has_secondary = cbuf.has_secondary, .cbuf_index = cbuf.index, .cbuf_offset = cbuf.offset, @@ -805,6 +808,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo .count = count, .size_shift = size_shift, }); + flags.is_integer.Assign(is_integer ? 1 : 0); } break; } diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index dfacc06802..0c11a0d72a 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -209,6 +209,7 @@ struct TextureDescriptor { TextureType type; bool is_depth; bool is_multisample; + bool is_integer; bool has_secondary; u32 cbuf_index; u32 cbuf_offset;