From 68593f9ddcd613d3aaa4c187dded836c52f20fdf Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Thu, 27 Nov 2025 16:18:15 -0400 Subject: [PATCH] [ir, spv] Added support for sampler component types in texture handling --- .../backend/spirv/emit_spirv_image.cpp | 43 +++++-- .../backend/spirv/spirv_emit_context.cpp | 36 ++++-- .../backend/spirv/spirv_emit_context.h | 2 + src/shader_recompiler/environment.h | 5 + src/shader_recompiler/ir_opt/texture_pass.cpp | 9 +- src/shader_recompiler/shader_info.h | 12 ++ src/video_core/shader_environment.cpp | 110 +++++++++++++++++- src/video_core/shader_environment.h | 8 ++ 8 files changed, 205 insertions(+), 20 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index a59b67cefa..07e8c3bc5b 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -198,6 +198,20 @@ 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: + case SamplerComponentType::Stencil: + return ctx.S32[4]; + case SamplerComponentType::Uint: + return ctx.U32[4]; + } + 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"); @@ -452,12 +466,15 @@ 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)}; 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()); + &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 @@ -465,17 +482,19 @@ 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); return Emit(&EmitContext::OpImageSparseSampleExplicitLod, - &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], - Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); + &EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, texture, coords, + operands.Mask(), operands.Span()); } } 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], + &EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); } @@ -512,12 +531,15 @@ 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), + color_type, texture, coords, ctx.Const(info.gather_component), operands.MaskOptional(), operands.Span()); } @@ -536,6 +558,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{}; @@ -546,7 +571,7 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c } const ImageOperands operands(lod, ms); return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, - ctx.F32[4], TextureImage(ctx, info, index), coords, operands.MaskOptional(), + result_type, TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); } @@ -592,13 +617,15 @@ 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], + &EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); } diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index 04045d0e64..d1a741a34f 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -28,27 +28,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}; - const Id type{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; } @@ -1363,7 +1376,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)}; @@ -1376,8 +1390,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, + .component_type = desc.component_type, }); 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 3d06fa6f89..1a252bf9f0 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h @@ -39,8 +39,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 { diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h index 5dbbc7e61e..a98bdc5262 100644 --- a/src/shader_recompiler/environment.h +++ b/src/shader_recompiler/environment.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 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 @@ -22,6 +25,8 @@ public: [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0; + [[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/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 9f04c0afaf..bd87ff150b 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -396,6 +396,10 @@ bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf)); } +SamplerComponentType ReadTextureComponentType(Environment& env, const ConstBufferAddr& cbuf) { + return env.ReadTextureComponentType(GetTextureHandle(env, cbuf)); +} + class Descriptors { public: explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_, @@ -433,7 +437,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 && @@ -670,6 +676,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, diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index ed13e68209..af38aefe74 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 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 @@ -151,6 +154,14 @@ enum class ImageFormat : u32 { R32G32B32A32_UINT, }; +enum class SamplerComponentType : u8 { + Float, + Sint, + Uint, + Depth, + Stencil, +}; + enum class Interpolation { Smooth, Flat, @@ -204,6 +215,7 @@ using ImageBufferDescriptors = boost::container::small_vector(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())}; @@ -207,6 +261,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)) @@ -223,6 +279,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)); @@ -374,6 +434,21 @@ Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { 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; } @@ -430,6 +505,20 @@ Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) { 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; } @@ -455,12 +544,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), + .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)) @@ -480,6 +572,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; @@ -534,6 +633,15 @@ u32 FileEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { 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::TextureType FileEnvironment::ReadTextureType(u32 handle) { const auto it{texture_types.find(handle)}; if (it == texture_types.end()) { diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 95c2d79277..b76a7466bf 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -80,6 +80,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; @@ -116,6 +117,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; @@ -142,6 +145,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; @@ -176,6 +181,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; @@ -202,6 +209,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;