Merge pull request 'feat(shaders): Proper Component Type Handling w/ various types of textures' (#133) from feat/shader-component-type-handling into main

Reviewed-on: https://git.citron-emu.org/Citron/Emulator/pulls/133
This commit is contained in:
Collecting
2026-02-10 22:58:11 +01:00
10 changed files with 468 additions and 44 deletions

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <boost/container/static_vector.hpp> #include <boost/container/static_vector.hpp>
@@ -195,6 +196,38 @@ 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:
case SamplerComponentType::Stencil:
return ctx.F32[4];
case SamplerComponentType::Sint:
return ctx.S32[4];
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:
case SamplerComponentType::Stencil:
return color;
case SamplerComponentType::Sint:
return ctx.OpConvertSToF(ctx.F32[4], color);
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) { Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& index) {
if (!index.IsImmediate() || index.U32() != 0) { if (!index.IsImmediate() || index.U32() != 0) {
throw NotImplementedException("Indirect image indexing"); throw NotImplementedException("Indirect image indexing");
@@ -449,31 +482,39 @@ Id EmitBoundImageWrite(EmitContext&) {
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id bias_lc, const IR::Value& offset) { Id bias_lc, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
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) { if (ctx.stage == Stage::Fragment) {
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
bias_lc, offset); bias_lc, offset);
return Emit(&EmitContext::OpImageSparseSampleImplicitLod, color = Emit(&EmitContext::OpImageSparseSampleImplicitLod,
&EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4], &EmitContext::OpImageSampleImplicitLod, ctx, inst, color_type, texture, coords,
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); operands.MaskOptional(), operands.Span());
} else { } else {
// We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as // 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 // if the lod was explicitly zero. This may change on Turing with implicit compute
// derivatives // derivatives
const Id lod{ctx.Const(0.0f)}; const Id lod{ctx.Const(0.0f)};
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset); const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod, color = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], &EmitContext::OpImageSampleExplicitLod, ctx, inst, color_type, texture, coords,
Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); operands.Mask(), operands.Span());
} }
return TextureSampleResultToFloat(ctx, def, color);
} }
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, const IR::Value& offset) { Id lod, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
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); const ImageOperands operands(ctx, false, true, false, lod, offset);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod, const Id color{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()); 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, Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
@@ -509,13 +550,17 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2) { const IR::Value& offset, const IR::Value& offset2) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const Id color_type{TextureColorResultType(ctx, def)};
const ImageOperands operands(ctx, offset, offset2); const ImageOperands operands(ctx, offset, offset2);
const Id texture{Texture(ctx, info, index)};
if (ctx.profile.need_gather_subpixel_offset) { if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords); coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
} }
return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, const Id color{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()); operands.MaskOptional(), operands.Span())};
return TextureSampleResultToFloat(ctx, def, color);
} }
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
@@ -533,6 +578,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 EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
Id lod, Id ms) { Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
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); AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) { if (info.type == TextureType::Buffer) {
lod = Id{}; lod = Id{};
@@ -542,8 +590,13 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
lod = Id{}; lod = Id{};
} }
const ImageOperands operands(lod, ms); const ImageOperands operands(lod, ms);
return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], Id color{Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst,
TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); 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, Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
@@ -588,14 +641,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 EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id derivatives, const IR::Value& offset, Id lod_clamp) { Id derivatives, const IR::Value& offset, Id lod_clamp) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
const Id color_type{TextureColorResultType(ctx, def)};
const auto operands = info.num_derivatives == 3 const auto operands = info.num_derivatives == 3
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
ctx.Def(offset), {}, lod_clamp) ctx.Def(offset), {}, lod_clamp)
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
info.num_derivatives, offset, lod_clamp); info.num_derivatives, offset, lod_clamp);
return Emit(&EmitContext::OpImageSparseSampleExplicitLod, const Id color{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()); 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) { Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {

View File

@@ -26,27 +26,43 @@ enum class Operation {
FPMax, 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:
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}; const spv::ImageFormat format{spv::ImageFormat::Unknown};
const Id type{ctx.F32[1]};
const bool depth{desc.is_depth}; const bool depth{desc.is_depth};
const bool ms{desc.is_multisample}; const bool ms{desc.is_multisample};
switch (desc.type) { switch (desc.type) {
case TextureType::Color1D: 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: 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::Color2D:
case TextureType::Color2DRect: 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: 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: 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: 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: 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: case TextureType::Buffer:
break; break;
} }
@@ -275,7 +291,7 @@ void Name(EmitContext& ctx, Id object, std::string_view format_str, Args&&... ar
.c_str()); .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) { u32 binding, Id type, char type_char, u32 element_size) {
const Id array_type{ctx.TypeArray(type, ctx.Const(65536U / element_size))}; const Id array_type{ctx.TypeArray(type, ctx.Const(65536U / element_size))};
ctx.Decorate(array_type, spv::Decoration::ArrayStride, element_size); ctx.Decorate(array_type, spv::Decoration::ArrayStride, element_size);
@@ -306,7 +322,7 @@ void DefineConstBuffers(EmitContext& ctx, const Info& info, Id UniformDefinition
} }
void DefineSsbos(EmitContext& ctx, StorageTypeDefinition& type_def, 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) { u32 stride) {
const Id array_type{ctx.TypeRuntimeArray(type)}; const Id array_type{ctx.TypeRuntimeArray(type)};
ctx.Decorate(array_type, spv::Decoration::ArrayStride, stride); ctx.Decorate(array_type, spv::Decoration::ArrayStride, stride);
@@ -325,6 +341,9 @@ void DefineSsbos(EmitContext& ctx, StorageTypeDefinition& type_def,
ctx.Decorate(id, spv::Decoration::Binding, binding); ctx.Decorate(id, spv::Decoration::Binding, binding);
ctx.Decorate(id, spv::Decoration::DescriptorSet, 0U); ctx.Decorate(id, spv::Decoration::DescriptorSet, 0U);
ctx.Name(id, fmt::format("ssbo{}", index)); ctx.Name(id, fmt::format("ssbo{}", index));
if (!desc.is_written) {
ctx.Decorate(id, spv::Decoration::NonWritable);
}
if (ctx.profile.supported_spirv >= 0x00010400) { if (ctx.profile.supported_spirv >= 0x00010400) {
ctx.interfaces.push_back(id); ctx.interfaces.push_back(id);
} }
@@ -556,6 +575,7 @@ void EmitContext::DefineCommonTypes(const Info& info) {
output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32"); output_f32 = Name(TypePointer(spv::StorageClass::Output, F32[1]), "output_f32");
output_u32 = Name(TypePointer(spv::StorageClass::Output, U32[1]), "output_u32"); 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) { if (info.uses_int8 && profile.support_int8) {
AddCapability(spv::Capability::Int8); AddCapability(spv::Capability::Int8);
@@ -1169,7 +1189,7 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
if (!info.uses_cbuf_indirect) { if (!info.uses_cbuf_indirect) {
return; 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_type{TypeFunction(buffer_type, U32[1], U32[1])};
const Id func{OpFunction(buffer_type, spv::FunctionControlMask::MaskNone, func_type)}; const Id func{OpFunction(buffer_type, spv::FunctionControlMask::MaskNone, func_type)};
const Id binding{OpFunctionParameter(U32[1])}; const Id binding{OpFunctionParameter(U32[1])};
@@ -1369,7 +1389,8 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_index) { void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_index) {
textures.reserve(info.texture_descriptors.size()); textures.reserve(info.texture_descriptors.size());
for (const TextureDescriptor& desc : info.texture_descriptors) { 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 sampled_type{TypeSampledImage(image_type)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)}; const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)};
const Id desc_type{DescType(*this, sampled_type, pointer_type, desc.count)}; const Id desc_type{DescType(*this, sampled_type, pointer_type, desc.count)};
@@ -1382,8 +1403,10 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
.sampled_type = sampled_type, .sampled_type = sampled_type,
.pointer_type = pointer_type, .pointer_type = pointer_type,
.image_type = image_type, .image_type = image_type,
.result_type = result_type,
.count = desc.count, .count = desc.count,
.is_multisample = desc.is_multisample, .is_multisample = desc.is_multisample,
.component_type = desc.component_type,
}); });
if (profile.supported_spirv >= 0x00010400) { if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id); interfaces.push_back(id);
@@ -1442,6 +1465,9 @@ void EmitContext::DefineInputs(const IR::Program& program) {
} }
if (info.uses_sample_id) { if (info.uses_sample_id) {
sample_id = DefineInput(*this, U32[1], false, spv::BuiltIn::SampleId); 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) { if (info.uses_is_helper_invocation) {
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation); 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_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR); subgroup_mask_gt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGtMaskKHR);
subgroup_mask_ge = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupGeMaskKHR); 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 || if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
(profile.warp_size_potentially_larger_than_guest && (profile.warp_size_potentially_larger_than_guest &&
@@ -1459,7 +1492,9 @@ void EmitContext::DefineInputs(const IR::Program& program) {
AddCapability(spv::Capability::GroupNonUniform); AddCapability(spv::Capability::GroupNonUniform);
subgroup_local_invocation_id = subgroup_local_invocation_id =
DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); 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) { if (info.uses_fswzadd) {
const Id f32_one{Const(1.0f)}; const Id f32_one{Const(1.0f)};
@@ -1471,6 +1506,9 @@ void EmitContext::DefineInputs(const IR::Program& program) {
} }
if (loads[IR::Attribute::PrimitiveId]) { if (loads[IR::Attribute::PrimitiveId]) {
primitive_id = DefineInput(*this, U32[1], false, spv::BuiltIn::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]) { if (loads[IR::Attribute::Layer]) {
AddCapability(spv::Capability::Geometry); AddCapability(spv::Capability::Geometry);
@@ -1562,17 +1600,23 @@ void EmitContext::DefineInputs(const IR::Program& program) {
if (stage != Stage::Fragment) { if (stage != Stage::Fragment) {
continue; continue;
} }
switch (info.interpolation[index]) { const bool is_integer =
case Interpolation::Smooth: input_type == AttributeType::SignedInt || input_type == AttributeType::UnsignedInt;
// Default if (is_integer) {
// Decorate(id, spv::Decoration::Smooth);
break;
case Interpolation::NoPerspective:
Decorate(id, spv::Decoration::NoPerspective);
break;
case Interpolation::Flat:
Decorate(id, spv::Decoration::Flat); 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) { if (stage == Stage::TessellationEval) {

View File

@@ -34,8 +34,10 @@ struct TextureDefinition {
Id sampled_type; Id sampled_type;
Id pointer_type; Id pointer_type;
Id image_type; Id image_type;
Id result_type;
u32 count; u32 count;
bool is_multisample; bool is_multisample;
SamplerComponentType component_type;
}; };
struct TextureBufferDefinition { struct TextureBufferDefinition {
@@ -67,7 +69,7 @@ struct UniformDefinitions {
Id U32x2{}; Id U32x2{};
Id U32x4{}; 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) { if (member_ptr == &UniformDefinitions::U8) {
return 1; return 1;
} }
@@ -96,7 +98,7 @@ struct UniformDefinitions {
return 1; return 1;
} }
constexpr static bool IsFloat(Id UniformDefinitions::*member_ptr) { constexpr static bool IsFloat(Id UniformDefinitions::* member_ptr) {
if (member_ptr == &UniformDefinitions::F32) { if (member_ptr == &UniformDefinitions::F32) {
return true; return true;
} }
@@ -242,6 +244,7 @@ public:
Id output_f32{}; Id output_f32{};
Id output_u32{}; Id output_u32{};
Id output_s32{};
Id image_buffer_type{}; Id image_buffer_type{};
Id image_u32{}; Id image_u32{};

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
@@ -22,6 +23,11 @@ public:
[[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0; [[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 TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0;
[[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0; [[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0;

View File

@@ -392,6 +392,11 @@ bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf)
return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf)); return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
} }
[[nodiscard]] SamplerComponentType ReadTextureComponentType(Environment& env,
const ConstBufferAddr& cbuf) {
return env.ReadTextureComponentType(GetTextureHandle(env, cbuf));
}
class Descriptors { class Descriptors {
public: public:
explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_, explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
@@ -429,7 +434,9 @@ public:
u32 Add(const TextureDescriptor& desc) { u32 Add(const TextureDescriptor& desc) {
const u32 index{Add(texture_descriptors, desc, [&desc](const auto& existing) { 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.has_secondary == existing.has_secondary &&
desc.cbuf_index == existing.cbuf_index && desc.cbuf_index == existing.cbuf_index &&
desc.cbuf_offset == existing.cbuf_offset && desc.cbuf_offset == existing.cbuf_offset &&
@@ -666,6 +673,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
} else { } else {
index = descriptors.Add(TextureDescriptor{ index = descriptors.Add(TextureDescriptor{
.type = flags.type, .type = flags.type,
.component_type = ReadTextureComponentType(env, cbuf),
.is_depth = flags.is_depth != 0, .is_depth = flags.is_depth != 0,
.is_multisample = is_multisample, .is_multisample = is_multisample,
.has_secondary = cbuf.has_secondary, .has_secondary = cbuf.has_secondary,

View File

@@ -158,6 +158,25 @@ enum class ImageFormat : u32 {
R32G32B32A32_SFLOAT, 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 { enum class Interpolation {
Smooth, Smooth,
Flat, Flat,
@@ -211,6 +230,7 @@ using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescrip
struct TextureDescriptor { struct TextureDescriptor {
TextureType type; TextureType type;
SamplerComponentType component_type;
bool is_depth; bool is_depth;
bool is_multisample; bool is_multisample;
bool has_secondary; bool has_secondary;

View File

@@ -19,6 +19,7 @@ set(SHADER_FILES
blit_color_float.frag blit_color_float.frag
block_linear_unswizzle_2d.comp block_linear_unswizzle_2d.comp
block_linear_unswizzle_3d.comp block_linear_unswizzle_3d.comp
block_linear_unswizzle_3d_bcn.comp
convert_abgr8_to_d24s8.frag convert_abgr8_to_d24s8.frag
convert_abgr8_to_d32f.frag convert_abgr8_to_d32f.frag
convert_d32f_to_abgr8.frag convert_d32f_to_abgr8.frag

View File

@@ -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;
}
}

View File

@@ -3,6 +3,7 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
#include <bitset>
#include <filesystem> #include <filesystem>
#include <fstream> #include <fstream>
#include <memory> #include <memory>
@@ -69,6 +70,58 @@ static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture
entry.a_type, entry.srgb_conversion)); 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) { static std::string_view StageToPrefix(Shader::Stage stage) {
switch (stage) { switch (stage) {
case Shader::Stage::VertexB: case Shader::Stage::VertexB:
@@ -199,6 +252,7 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
const u64 code_size{static_cast<u64>(CachedSizeBytes())}; const u64 code_size{static_cast<u64>(CachedSizeBytes())};
const u64 num_texture_types{static_cast<u64>(texture_types.size())}; const u64 num_texture_types{static_cast<u64>(texture_types.size())};
const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())}; const u64 num_texture_pixel_formats{static_cast<u64>(texture_pixel_formats.size())};
const u64 num_texture_component_types{static_cast<u64>(texture_component_types.size())};
const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())}; const u64 num_cbuf_values{static_cast<u64>(cbuf_values.size())};
const u64 num_cbuf_replacement_values{static_cast<u64>(cbuf_replacements.size())}; const u64 num_cbuf_replacement_values{static_cast<u64>(cbuf_replacements.size())};
@@ -206,6 +260,8 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
.write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types)) .write(reinterpret_cast<const char*>(&num_texture_types), sizeof(num_texture_types))
.write(reinterpret_cast<const char*>(&num_texture_pixel_formats), .write(reinterpret_cast<const char*>(&num_texture_pixel_formats),
sizeof(num_texture_pixel_formats)) sizeof(num_texture_pixel_formats))
.write(reinterpret_cast<const char*>(&num_texture_component_types),
sizeof(num_texture_component_types))
.write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values)) .write(reinterpret_cast<const char*>(&num_cbuf_values), sizeof(num_cbuf_values))
.write(reinterpret_cast<const char*>(&num_cbuf_replacement_values), .write(reinterpret_cast<const char*>(&num_cbuf_replacement_values),
sizeof(num_cbuf_replacement_values)) sizeof(num_cbuf_replacement_values))
@@ -222,6 +278,10 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
file.write(reinterpret_cast<const char*>(&key), sizeof(key)) file.write(reinterpret_cast<const char*>(&key), sizeof(key))
.write(reinterpret_cast<const char*>(&type), sizeof(type)); .write(reinterpret_cast<const char*>(&type), sizeof(type));
} }
for (const auto& [key, component] : texture_component_types) {
file.write(reinterpret_cast<const char*>(&key), sizeof(key))
.write(reinterpret_cast<const char*>(&component), sizeof(component));
}
for (const auto& [key, format] : texture_pixel_formats) { for (const auto& [key, format] : texture_pixel_formats) {
file.write(reinterpret_cast<const char*>(&key), sizeof(key)) file.write(reinterpret_cast<const char*>(&key), sizeof(key))
.write(reinterpret_cast<const char*>(&format), sizeof(format)); .write(reinterpret_cast<const char*>(&format), sizeof(format));
@@ -406,12 +466,31 @@ std::optional<Shader::ReplaceConstant> GraphicsEnvironment::GetReplaceConstBuffe
} }
Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) { 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 auto& regs{maxwell3d->regs};
const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding}; const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
auto entry = auto entry =
ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle); ReadTextureInfo(regs.tex_header.Address(), regs.tex_header.limit, via_header_index, handle);
const Shader::TextureType result{ConvertTextureType(entry)}; const Shader::TextureType result{ConvertTextureType(entry)};
texture_types.emplace(handle, result); 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; return result;
} }
@@ -463,11 +542,29 @@ u32 ComputeEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) {
} }
Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) { 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& regs{kepler_compute->regs};
const auto& qmd{kepler_compute->launch_description}; const auto& qmd{kepler_compute->launch_description};
auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); auto entry = ReadTextureInfo(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle);
const Shader::TextureType result{ConvertTextureType(entry)}; const Shader::TextureType result{ConvertTextureType(entry)};
texture_types.emplace(handle, result); 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; return result;
} }
@@ -493,12 +590,15 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
u64 code_size{}; u64 code_size{};
u64 num_texture_types{}; u64 num_texture_types{};
u64 num_texture_pixel_formats{}; u64 num_texture_pixel_formats{};
u64 num_texture_component_types{};
u64 num_cbuf_values{}; u64 num_cbuf_values{};
u64 num_cbuf_replacement_values{}; u64 num_cbuf_replacement_values{};
file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size)) file.read(reinterpret_cast<char*>(&code_size), sizeof(code_size))
.read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types)) .read(reinterpret_cast<char*>(&num_texture_types), sizeof(num_texture_types))
.read(reinterpret_cast<char*>(&num_texture_pixel_formats), .read(reinterpret_cast<char*>(&num_texture_pixel_formats),
sizeof(num_texture_pixel_formats)) sizeof(num_texture_pixel_formats))
.read(reinterpret_cast<char*>(&num_texture_component_types),
sizeof(num_texture_component_types))
.read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values)) .read(reinterpret_cast<char*>(&num_cbuf_values), sizeof(num_cbuf_values))
.read(reinterpret_cast<char*>(&num_cbuf_replacement_values), .read(reinterpret_cast<char*>(&num_cbuf_replacement_values),
sizeof(num_cbuf_replacement_values)) sizeof(num_cbuf_replacement_values))
@@ -518,6 +618,13 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
.read(reinterpret_cast<char*>(&type), sizeof(type)); .read(reinterpret_cast<char*>(&type), sizeof(type));
texture_types.emplace(key, 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<char*>(&key), sizeof(key))
.read(reinterpret_cast<char*>(&component), sizeof(component));
texture_component_types.emplace(key, component);
}
for (size_t i = 0; i < num_texture_pixel_formats; ++i) { for (size_t i = 0; i < num_texture_pixel_formats; ++i) {
u32 key; u32 key;
Shader::TexturePixelFormat format; Shader::TexturePixelFormat format;
@@ -580,6 +687,15 @@ Shader::TextureType FileEnvironment::ReadTextureType(u32 handle) {
return it->second; 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) { Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) {
const auto it{texture_pixel_formats.find(handle)}; const auto it{texture_pixel_formats.find(handle)};
if (it == texture_pixel_formats.end()) { if (it == texture_pixel_formats.end()) {

View File

@@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
@@ -77,6 +78,7 @@ protected:
std::vector<u64> code; std::vector<u64> code;
std::unordered_map<u32, Shader::TextureType> texture_types; std::unordered_map<u32, Shader::TextureType> texture_types;
std::unordered_map<u32, Shader::SamplerComponentType> texture_component_types;
std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
std::unordered_map<u64, u32> cbuf_values; std::unordered_map<u64, u32> cbuf_values;
std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements; std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;
@@ -113,6 +115,8 @@ public:
Shader::TextureType ReadTextureType(u32 handle) override; Shader::TextureType ReadTextureType(u32 handle) override;
Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override;
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
bool IsTexturePixelFormatInteger(u32 handle) override; bool IsTexturePixelFormatInteger(u32 handle) override;
@@ -139,6 +143,8 @@ public:
Shader::TextureType ReadTextureType(u32 handle) override; Shader::TextureType ReadTextureType(u32 handle) override;
Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override;
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
bool IsTexturePixelFormatInteger(u32 handle) override; bool IsTexturePixelFormatInteger(u32 handle) override;
@@ -173,6 +179,8 @@ public:
[[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override; [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override;
[[nodiscard]] Shader::SamplerComponentType ReadTextureComponentType(u32 handle) override;
[[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
[[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override; [[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override;
@@ -199,6 +207,7 @@ public:
private: private:
std::vector<u64> code; std::vector<u64> code;
std::unordered_map<u32, Shader::TextureType> texture_types; std::unordered_map<u32, Shader::TextureType> texture_types;
std::unordered_map<u32, Shader::SamplerComponentType> texture_component_types;
std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats; std::unordered_map<u32, Shader::TexturePixelFormat> texture_pixel_formats;
std::unordered_map<u64, u32> cbuf_values; std::unordered_map<u64, u32> cbuf_values;
std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements; std::unordered_map<u64, Shader::ReplaceConstant> cbuf_replacements;