shader: reflect HS patch constants and lower TES patch inputs through predicated addresses

This commit is contained in:
Mr. Big Britches
2026-10-20 08:04:50 +02:00
committed by nmzik
parent 4a2961cdf9
commit 0957f088ad
6 changed files with 109 additions and 60 deletions
@@ -104,9 +104,8 @@ struct EmitterState {
std::array<uint32_t, 3> u32_vector_types {};
std::array<uint32_t, 3> i32_vector_types {};
std::array<uint32_t, 3> f32_vector_types {};
std::array<uint32_t, 6> tess_variables {};
std::array<uint32_t, IR::NumTessellationAttributes> tess_variables {};
uint32_t tess_inner_variable = 0;
uint32_t tess_patch_base = 0;
uint32_t lane_count = 1;
uint32_t lane_half = 0;
@@ -5,6 +5,17 @@
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
namespace {
bool IsTessellationInput(IR::TessellationAttribute kind) {
using Attribute = IR::TessellationAttribute;
return kind == Attribute::ControlInput || kind == Attribute::EvaluationInput ||
kind == Attribute::PatchInput;
}
bool IsTessellationPatch(IR::TessellationAttribute kind) {
using Attribute = IR::TessellationAttribute;
return kind == Attribute::PatchOutput || kind == Attribute::PatchInput;
}
uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
auto& state = ctx.state;
using Attribute = IR::TessellationAttribute;
@@ -12,8 +23,9 @@ uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
const auto& tess = state.input_info.vertex->tess;
const auto variable = state.tess_variables.at(static_cast<uint32_t>(kind));
EXIT_IF(variable == 0);
const auto input = kind == Attribute::ControlInput || kind == Attribute::EvaluationInput;
const auto storage = input ? spv::StorageClassInput : spv::StorageClassOutput;
const bool patch = IsTessellationPatch(kind);
const auto storage =
IsTessellationInput(kind) ? spv::StorageClassInput : spv::StorageClassOutput;
const auto pointer = state.builder.AllocateId();
if (kind == Attribute::Factor) {
EXIT_NOT_IMPLEMENTED(!inst.Arg(1).IsImmediate());
@@ -26,15 +38,13 @@ uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
return pointer;
}
auto address = ctx.Arg(inst, 1);
if (kind == Attribute::PatchOutput) {
address =
EmitBinaryU32(state, spv::OpISub, address, ConstantU32(state, state.tess_patch_base));
if (patch) {
address = EmitBinaryU32(state, spv::OpISub, address, ConstantU32(state, tess.patch_base));
}
const bool local = kind == Attribute::LocalOutput || kind == Attribute::ControlInput;
const auto stride = local ? tess.ls_stride : tess.hs_stride;
const auto offset = kind == Attribute::PatchOutput ? address
: EmitBinaryU32(state, spv::OpUMod, address,
ConstantU32(state, stride));
const auto offset =
patch ? address : EmitBinaryU32(state, spv::OpUMod, address, ConstantU32(state, stride));
const auto attribute =
EmitBinaryU32(state, spv::OpShiftRightLogical, offset, ConstantU32(state, 4));
const auto component =
@@ -42,7 +52,7 @@ uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
EmitBinaryU32(state, spv::OpShiftRightLogical, offset, ConstantU32(state, 2)),
ConstantU32(state, 3));
const auto type = TypePointer(state, storage, TypeU32(state));
if (kind == Attribute::LocalOutput || kind == Attribute::PatchOutput) {
if (kind == Attribute::LocalOutput || patch) {
state.builder.AddFunction(spv::OpAccessChain, type, pointer, variable, attribute,
component);
} else {
@@ -60,20 +70,12 @@ uint32_t TessellationPointer(ValueEmitContext& ctx, const IR::Inst& inst) {
void DefineTessellationInterfaces(EmitterState& state) {
using Attribute = IR::TessellationAttribute;
std::array<bool, 6> used {};
uint32_t patch_begin = UINT32_MAX, patch_end = 0;
std::array<bool, IR::NumTessellationAttributes> used {};
for (const auto* block: state.program.blocks) {
for (const auto& inst: *block) {
if (inst.GetOpcode() != IR::ValueOpcode::GetTessellationAttribute &&
inst.GetOpcode() != IR::ValueOpcode::SetTessellationAttribute)
continue;
const auto kind = inst.Arg(0).U32();
used.at(kind) = true;
if (kind == static_cast<uint32_t>(Attribute::PatchOutput)) {
EXIT_NOT_IMPLEMENTED(!inst.Arg(1).IsImmediate());
patch_begin = std::min(patch_begin, inst.Arg(1).U32());
patch_end = std::max(patch_end, inst.Arg(1).U32() + 4u);
}
if (inst.GetOpcode() == IR::ValueOpcode::GetTessellationAttribute ||
inst.GetOpcode() == IR::ValueOpcode::SetTessellationAttribute)
used.at(inst.Arg(0).U32()) = true;
}
}
if (std::ranges::none_of(used, [](bool value) { return value; })) return;
@@ -83,15 +85,16 @@ void DefineTessellationInterfaces(EmitterState& state) {
};
for (uint32_t index = 0; index < used.size(); index++) {
if (!used[index]) continue;
const auto kind = static_cast<Attribute>(index);
const bool input = kind == Attribute::ControlInput || kind == Attribute::EvaluationInput;
const auto storage = input ? spv::StorageClassInput : spv::StorageClassOutput;
uint32_t type;
const auto kind = static_cast<Attribute>(index);
const bool patch = IsTessellationPatch(kind);
const auto storage =
IsTessellationInput(kind) ? spv::StorageClassInput : spv::StorageClassOutput;
uint32_t type;
if (kind == Attribute::Factor) {
type = array(TypeF32(state), 4u);
} else if (kind == Attribute::PatchOutput) {
state.tess_patch_base = patch_begin;
type = array(TypeU32Vector(state, 4), (patch_end - patch_begin + 15u) / 16u);
} else if (patch) {
// HS and TES share the reflected patch layout so their interfaces match.
type = array(TypeU32Vector(state, 4), (tess.patch_size + 15u) / 16u);
} else {
const bool local = kind == Attribute::LocalOutput || kind == Attribute::ControlInput;
const auto stride = local ? tess.ls_stride : tess.hs_stride;
@@ -112,11 +115,10 @@ void DefineTessellationInterfaces(EmitterState& state) {
state.builder.AddAnnotation(spv::OpDecorate, state.tess_inner_variable,
spv::DecorationPatch);
} else {
state.builder.AddAnnotation(
spv::OpDecorate, variable, spv::DecorationLocation,
kind == Attribute::PatchOutput ? (tess.hs_stride + 15u) / 16u : 0u);
state.builder.AddAnnotation(spv::OpDecorate, variable, spv::DecorationLocation,
patch ? (tess.hs_stride + 15u) / 16u : 0u);
}
if (kind == Attribute::Factor || kind == Attribute::PatchOutput) {
if (kind == Attribute::Factor || patch) {
state.builder.AddAnnotation(spv::OpDecorate, variable, spv::DecorationPatch);
}
}