shader: run merged geometry stages as mesh shaders

Co-authored-by: Brandon Strong <brandostrong@gmail.com>
Co-authored-by: fxpw <fxpw@outlook.com>
Co-authored-by: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com>
This commit is contained in:
nmzik
2026-09-05 08:32:23 +04:00
parent 246212789f
commit 3213934d42
39 changed files with 1403 additions and 858 deletions
@@ -28,17 +28,23 @@ void Translator::S_SAVEEXEC(const Decoder::Instruction& inst, IR::ValueOpcode op
const auto src = ReadMask(inst.src0);
const auto lhs = negate_exec ? ir.LogicalNot(old) : old;
const auto rhs = negate_source ? ir.LogicalNot(src) : src;
const auto result = IR::U1(ir.Emit(operation, {lhs, rhs}));
auto result = IR::U1(ir.Emit(operation, {lhs, rhs}));
if (write_64) {
WriteMask64(inst.dst, old);
WriteMask(inst.dst, old, true);
} else {
WriteMask(inst.dst, old);
WriteRawU32(inst.dst, ir.GetExecLo());
if (current_wave_size == 64u) {
const auto low_half =
ir.ULessThan(IR::U32(ir.Emit(IR::ValueOpcode::LaneId)), IR::U32(IR::Value(32u)));
result = IR::U1(ir.Emit(IR::ValueOpcode::SelectU1, {low_half, result, old}));
}
}
const auto mask = BallotMask(result);
ir.SetExec(result);
ir.SetExecLo(mask[0]);
ir.SetExecHi(mask[1]);
ir.SetScc(result);
ir.SetScc(
ir.INotEqual(write_64 ? ir.BitwiseOr(mask[0], mask[1]) : mask[0], IR::U32(IR::Value(0u))));
}
void Translator::ADD_U32(const Decoder::Instruction& inst, bool vector, bool use_carry_in) {
@@ -149,8 +155,13 @@ void Translator::S_BARRIER() {
ir.Emit(IR::ValueOpcode::Barrier);
}
void Translator::S_SENDMSG() {
ir.Emit(IR::ValueOpcode::Sendmsg);
void Translator::S_SENDMSG(const Decoder::Instruction& inst) {
if (program.stage == ShaderType::Mesh) {
EXIT_NOT_IMPLEMENTED(inst.src0.value != 9u); // MSG_GS_ALLOC_REQ
ir.Emit(IR::ValueOpcode::MeshAllocate, {ir.GetM0()});
} else {
ir.Emit(IR::ValueOpcode::Sendmsg);
}
}
void Translator::S_TTRACEDATA() {
@@ -207,7 +218,7 @@ void Translator::S_CSELECT_B64(const Decoder::Instruction& inst) {
IR::U1(ir.Emit(IR::ValueOpcode::SelectU1,
{condition, ReadMaskValid(inst.src0), ReadMaskValid(inst.src1)}));
if (IsExecOrVcc(inst.dst)) {
WriteMask64(inst.dst, selected_mask);
WriteMask(inst.dst, selected_mask, true);
return;
}
WriteU32Pair(inst.dst,
@@ -220,9 +231,7 @@ void Translator::S_CSELECT_B64(const Decoder::Instruction& inst) {
}
void Translator::MOV_B32(const Decoder::Instruction& inst, bool apply_float_modifiers) {
if (IsExecOrVcc(inst.src0) && IsExecOrVcc(inst.dst)) {
WriteMask(inst.dst, ReadMask(inst.src0));
} else if (apply_float_modifiers && (inst.src0.negate || inst.src0.absolute)) {
if (apply_float_modifiers && (inst.src0.negate || inst.src0.absolute)) {
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::F32));
} else {
WriteOperand(DestinationOperand(inst), ReadOperand(inst.src0, IR::Type::U32));
@@ -231,7 +240,7 @@ void Translator::MOV_B32(const Decoder::Instruction& inst, bool apply_float_modi
void Translator::S_MOV_B64(const Decoder::Instruction& inst) {
if (IsExecOrVcc(inst.dst) || IsExecOrVcc(inst.src0)) {
WriteMask64(inst.dst, ReadMask(inst.src0));
WriteMask(inst.dst, ReadMask(inst.src0), true);
return;
}
const bool scalar_copy =
@@ -250,30 +259,16 @@ void Translator::S_MOV_B64(const Decoder::Instruction& inst) {
}
void Translator::S_WQM_B64(const Decoder::Instruction& inst) {
if (!IsExecOrVcc(inst.dst) && !IsExecOrVcc(inst.src0)) {
const auto mask_valid = ReadMaskValid(inst.src0);
const auto invocation_result =
IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
const auto result =
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
WriteOperand(DestinationOperand(inst), result);
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, invocation_result);
ir.SetScalarMaskTag(dst, mask_valid);
const auto raw_nonzero =
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})}));
ir.SetScc(IR::U1(
ir.Emit(IR::ValueOpcode::SelectU1, {mask_valid, invocation_result, raw_nonzero})));
} else {
ir.SetScc(
IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
}
return;
const auto mask_valid = ReadMaskValid(inst.src0);
const auto result =
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
WriteOperand(DestinationOperand(inst), result);
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, ThreadBit(ExtractU64(result)));
ir.SetScalarMaskTag(dst, mask_valid);
}
const auto result = IR::U1(ir.Emit(IR::ValueOpcode::WqmMask, {ReadMask(inst.src0)}));
WriteMask64(inst.dst, result);
ir.SetScc(result);
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
}
void Translator::V_MOVRELS_B32(const Decoder::Instruction& inst) {
@@ -105,8 +105,8 @@ bool Translator::S_U64_MASK(const Decoder::Instruction& inst, IR::ValueOpcode lo
};
if (is_exec_or_vcc(inst.dst) || is_exec_or_vcc(inst.src0) ||
(inst.src_count > 1u && is_exec_or_vcc(inst.src1))) {
WriteMask64(inst.dst, invocation_result);
ir.SetScc(invocation_result);
const auto mask = WriteMask(inst.dst, invocation_result, true);
ir.SetScc(ir.INotEqual(ir.BitwiseOr(mask[0], mask[1]), IR::U32(IR::Value(0u))));
return true;
}
@@ -132,13 +132,8 @@ bool Translator::S_U64_MASK(const Decoder::Instruction& inst, IR::ValueOpcode lo
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
ir.SetThreadBitScalarReg(dst, invocation_result);
ir.SetScalarMaskTag(dst, mask_valid);
const auto raw_nonzero =
ir.INotEqual(ir.BitwiseOr(result[0], result[1]), IR::U32(IR::Value(0u)));
ir.SetScc(IR::U1(
ir.Emit(IR::ValueOpcode::SelectU1, {mask_valid, invocation_result, raw_nonzero})));
} else {
ir.SetScc(ir.INotEqual(ir.BitwiseOr(result[0], result[1]), IR::U32(IR::Value(0u))));
}
ir.SetScc(ir.INotEqual(ir.BitwiseOr(result[0], result[1]), IR::U32(IR::Value(0u))));
return true;
}
@@ -208,7 +208,7 @@ bool Translator::EmitScalar(const Decoder::Instruction& inst) {
case O::S_TRAP: EmitControlNop(); return true;
case O::S_WAITCNT_DEPCTR: EmitWaitcnt(); return true;
case O::S_BARRIER: S_BARRIER(); return true;
case O::S_SENDMSG: S_SENDMSG(); return true;
case O::S_SENDMSG: S_SENDMSG(inst); return true;
case O::S_TTRACEDATA: S_TTRACEDATA(); return true;
case O::S_INST_PREFETCH: S_INST_PREFETCH(); return true;
case O::S_BRANCH:
@@ -110,8 +110,9 @@ Decoder::Operand Translator::PlainOperand(const Decoder::Operand& operand) {
}
std::array<IR::U32, 2> Translator::BallotMask(IR::U1 value) {
return {ir.Select(value, IR::U32(IR::Value(1u)), IR::U32(IR::Value(0u))),
IR::U32(IR::Value(0u))};
const auto mask = ir.Emit(IR::ValueOpcode::Ballot, {value});
return {ir.CompositeExtract(mask, 0),
current_wave_size == 64u ? ir.CompositeExtract(mask, 1) : IR::U32(IR::Value(0u))};
}
IR::U32 Translator::ReadRawU32(const Decoder::Operand& operand) {
@@ -285,7 +286,6 @@ void Translator::WriteRawU32(const Decoder::Operand& operand, IR::U32 value) {
case Decoder::OperandKind::Sgpr: {
const auto reg = static_cast<IR::ScalarReg>(operand.reg);
ir.SetScalarReg(reg, value);
ir.SetThreadBitScalarReg(reg, ThreadBit(value));
ir.SetScalarMaskTag(reg, IR::U1(IR::Value(false)));
if (IR::RegIndex(reg) > 0u) {
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(IR::RegIndex(reg) - 1u),
@@ -314,20 +314,20 @@ void Translator::WriteRawU32(const Decoder::Operand& operand, IR::U32 value) {
}
case Decoder::OperandKind::VccLo:
ir.SetVccLo(IR::U32(value));
ir.SetVcc(ThreadBit(IR::U32(value)));
ir.SetVcc(ThreadBit({value, ir.GetVccHi()}));
break;
case Decoder::OperandKind::VccHi:
ir.SetVccHi(IR::U32(value));
ir.SetVcc(ThreadBit(ir.GetVccLo()));
ir.SetVcc(ThreadBit({ir.GetVccLo(), value}));
break;
case Decoder::OperandKind::M0: ir.SetM0(IR::U32(value)); break;
case Decoder::OperandKind::ExecLo:
ir.SetExecLo(IR::U32(value));
ir.SetExec(ThreadBit(IR::U32(value)));
ir.SetExec(ThreadBit({value, ir.GetExecHi()}));
break;
case Decoder::OperandKind::ExecHi:
ir.SetExecHi(IR::U32(value));
ir.SetExec(ThreadBit(ir.GetExecLo()));
ir.SetExec(ThreadBit({ir.GetExecLo(), value}));
break;
case Decoder::OperandKind::Scc:
ir.SetScc(ir.INotEqual(value, IR::U32(IR::Value(0u))));
@@ -576,15 +576,14 @@ void Translator::WriteU32Pair(const Decoder::Operand& operand,
if (operand.kind == Decoder::OperandKind::Null) {
return;
}
const auto thread_bit = ThreadBit(value[0]);
switch (operand.kind) {
case Decoder::OperandKind::ExecLo:
ir.SetExec(thread_bit);
ir.SetExec(ThreadBit(value));
ir.SetExecLo(value[0]);
ir.SetExecHi(value[1]);
return;
case Decoder::OperandKind::VccLo:
ir.SetVcc(thread_bit);
ir.SetVcc(ThreadBit(value));
ir.SetVccLo(value[0]);
ir.SetVccHi(value[1]);
return;
@@ -595,8 +594,14 @@ void Translator::WriteU32Pair(const Decoder::Operand& operand,
WriteRawU32(OffsetOperand(operand, 1), value[1]);
}
IR::U1 Translator::ThreadBit(IR::U32 low) {
return ir.INotEqual(low, IR::U32(IR::Value(0u)));
IR::U1 Translator::ThreadBit(const std::array<IR::U32, 2>& mask) {
const auto lane = IR::U32(ir.Emit(IR::ValueOpcode::LaneId));
const auto word = current_wave_size == 64u
? ir.Select(ir.ULessThan(lane, IR::U32(IR::Value(32u))), mask[0], mask[1])
: mask[0];
const auto bit = ir.BitwiseAnd(lane, IR::U32(IR::Value(31u)));
return ir.INotEqual(ir.BitwiseAnd(ir.ShiftRightLogical(word, bit), IR::U32(IR::Value(1u))),
IR::U32(IR::Value(0u)));
}
IR::U1 Translator::ReadCondition(const Decoder::Operand& operand) {
@@ -604,11 +609,6 @@ IR::U1 Translator::ReadCondition(const Decoder::Operand& operand) {
}
IR::U32 Translator::ConditionBit(const Decoder::Operand& operand) {
if (operand.kind == Decoder::OperandKind::LiteralConstant ||
operand.kind == Decoder::OperandKind::IntegerInlineConstant ||
operand.kind == Decoder::OperandKind::FloatInlineConstant) {
return IR::U32(IR::Value(operand.value & 1u));
}
return ir.Select(ReadMask(operand), IR::U32(IR::Value(1u)), IR::U32(IR::Value(0u)));
}
@@ -616,12 +616,17 @@ IR::U1 Translator::ReadMask(const Decoder::Operand& operand) {
if (operand.kind == Decoder::OperandKind::LiteralConstant ||
operand.kind == Decoder::OperandKind::IntegerInlineConstant ||
operand.kind == Decoder::OperandKind::FloatInlineConstant) {
return IR::U1(IR::Value(operand.value != 0u));
return ThreadBit(ReadU32Pair(operand));
}
switch (operand.kind) {
case Decoder::OperandKind::Sgpr: {
const auto reg = static_cast<IR::ScalarReg>(operand.reg);
return ir.GetThreadBitScalarReg(reg);
const auto mask = current_wave_size == 64u
? ReadU32Pair(operand)
: std::array {ReadRawU32(operand), IR::U32(IR::Value(0u))};
return IR::U1(ir.Emit(
IR::ValueOpcode::SelectU1,
{ir.GetScalarMaskTag(reg), ir.GetThreadBitScalarReg(reg), ThreadBit(mask)}));
}
case Decoder::OperandKind::ExecLo:
case Decoder::OperandKind::ExecHi: return ir.GetExec();
@@ -660,11 +665,12 @@ IR::U1 Translator::ReadMaskValid(const Decoder::Operand& operand) {
}
}
void Translator::WriteMask(const Decoder::Operand& operand, IR::U1 value) {
std::array<IR::U32, 2> Translator::WriteMask(const Decoder::Operand& operand, IR::U1 value,
bool write_64) {
const auto mask = BallotMask(value);
switch (operand.kind) {
case Decoder::OperandKind::Sgpr: {
const auto reg = static_cast<IR::ScalarReg>(operand.reg);
const auto mask = BallotMask(value);
ir.SetThreadBitScalarReg(reg, value);
ir.SetScalarMaskTag(reg, IR::U1(IR::Value(true)));
if (IR::RegIndex(reg) > 0u) {
@@ -673,57 +679,34 @@ void Translator::WriteMask(const Decoder::Operand& operand, IR::U1 value) {
}
ir.SetScalarReg(reg, mask[0]);
// A wave32 VALU mask destination must not overwrite the neighboring SGPR.
if (current_wave_size == 64u && IR::RegIndex(reg) + 1u < IR::NumScalarRegs) {
if ((write_64 || current_wave_size == 64u) &&
IR::RegIndex(reg) + 1u < IR::NumScalarRegs) {
const auto high = static_cast<IR::ScalarReg>(IR::RegIndex(reg) + 1u);
ir.SetScalarReg(high, mask[1]);
ir.SetThreadBitScalarReg(high, IR::U1(IR::Value(false)));
ir.SetScalarMaskTag(high, IR::U1(IR::Value(false)));
}
return;
return mask;
}
case Decoder::OperandKind::ExecLo:
case Decoder::OperandKind::ExecHi: {
const auto mask = BallotMask(value);
ir.SetExec(value);
ir.SetExecLo(mask[0]);
ir.SetExecHi(mask[1]);
return;
return mask;
}
case Decoder::OperandKind::VccLo:
case Decoder::OperandKind::VccHi: {
const auto mask = BallotMask(value);
ir.SetVcc(value);
ir.SetVccLo(mask[0]);
ir.SetVccHi(mask[1]);
return;
return mask;
}
case Decoder::OperandKind::Scc: ir.SetScc(value); return;
case Decoder::OperandKind::Scc: ir.SetScc(value); return mask;
default:
WriteRawU32(operand, ir.Select(value, IR::U32(IR::Value(1u)), IR::U32(IR::Value(0u))));
WriteRawU32(OffsetOperand(operand, 1), IR::U32(IR::Value(0u)));
return;
}
}
void Translator::WriteMask64(const Decoder::Operand& operand, IR::U1 value) {
if (operand.kind != Decoder::OperandKind::Sgpr) {
WriteMask(operand, value);
return;
}
const auto reg = static_cast<IR::ScalarReg>(operand.reg);
const auto mask = BallotMask(value);
ir.SetThreadBitScalarReg(reg, value);
ir.SetScalarMaskTag(reg, IR::U1(IR::Value(true)));
if (IR::RegIndex(reg) > 0u) {
ir.SetScalarMaskTag(static_cast<IR::ScalarReg>(IR::RegIndex(reg) - 1u),
IR::U1(IR::Value(false)));
}
ir.SetScalarReg(reg, mask[0]);
if (IR::RegIndex(reg) + 1u < IR::NumScalarRegs) {
const auto high = static_cast<IR::ScalarReg>(IR::RegIndex(reg) + 1u);
ir.SetScalarReg(high, mask[1]);
ir.SetThreadBitScalarReg(high, IR::U1(IR::Value(false)));
ir.SetScalarMaskTag(high, IR::U1(IR::Value(false)));
WriteRawU32(operand, mask[0]);
WriteRawU32(OffsetOperand(operand, 1), mask[1]);
return mask;
}
}
@@ -910,6 +893,7 @@ void ValidateTranslateOptions(const TranslateOptions& options) {
}
switch (options.stage) {
case ShaderType::Vertex:
case ShaderType::Mesh:
if (options.vertex == nullptr) {
EXIT("vertex shader translation has no vertex input metadata");
}
@@ -1022,12 +1006,32 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
}
const auto value = entry_ir.GetUserData(reg);
entry_ir.SetScalarReg(reg, value);
entry_ir.SetThreadBitScalarReg(reg, entry_ir.INotEqual(value, IR::U32(IR::Value(0u))));
entry_ir.SetScalarMaskTag(reg, IR::U1(IR::Value(false)));
}
entry_ir.SetExec(IR::U1(IR::Value(true)));
entry_ir.SetExecLo(IR::U32(IR::Value(1u)));
entry_ir.SetExecHi(IR::U32(IR::Value(0u)));
auto initial_exec = IR::U1(IR::Value(true));
uint32_t total_threads = 0;
ShaderStageInputInfo stage_input {};
if (options.stage == ShaderType::Compute) {
stage_input.compute = options.compute;
} else {
stage_input.vertex = options.vertex;
}
const auto* workgroup = ShaderWorkgroupInput(options.stage, stage_input);
if (workgroup != nullptr) {
total_threads = std::max(workgroup->threads_num[0], 1u) *
std::max(workgroup->threads_num[1], 1u) *
std::max(workgroup->threads_num[2], 1u);
if (options.wave_size == 64u && workgroup->host_subgroup_size == 32u &&
total_threads % 64u != 0u) {
initial_exec = entry_ir.ULessThan(builtin(IR::StageInputKind::LocalInvocationIndex),
IR::U32(IR::Value(total_threads)));
}
}
entry_ir.SetExec(initial_exec);
const auto initial_mask = entry_ir.Emit(IR::ValueOpcode::Ballot, {initial_exec});
entry_ir.SetExecLo(entry_ir.CompositeExtract(initial_mask, 0));
entry_ir.SetExecHi(options.wave_size == 64u ? entry_ir.CompositeExtract(initial_mask, 1)
: IR::U32(IR::Value(0u)));
if (options.stage == ShaderType::Compute) {
const auto* cs = options.compute;
const auto thread_ids =
@@ -1046,9 +1050,6 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
}
if (cs->tg_size_en) {
const auto wave_size = cs->wave_size != 0u ? cs->wave_size : 64u;
const auto total_threads = std::max(cs->threads_num[0], 1u) *
std::max(cs->threads_num[1], 1u) *
std::max(cs->threads_num[2], 1u);
const auto waves = std::min((total_threads + wave_size - 1u) / wave_size, 0x3fu);
const auto local_index = builtin(IR::StageInputKind::LocalInvocationIndex);
const auto wave_id = IR::U32(
@@ -1062,6 +1063,53 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
entry_ir.BitwiseOr(entry_ir.BitwiseOr(wave_bits, IR::U32(IR::Value(waves))),
first_bit));
}
} else if (options.stage == ShaderType::Mesh) {
const auto& mesh = options.vertex->mesh;
EXIT_NOT_IMPLEMENTED(options.wave_size != 64u || mesh.primitives_per_group == 0u ||
mesh.vertices_per_group > 64u || total_threads > 15u * 64u);
const auto u32 = [](uint32_t value) { return IR::U32(IR::Value(value)); };
const auto draw = [&](uint32_t index) {
return IR::U32(
entry_ir.Emit(IR::ValueOpcode::MeshDrawParameter, {IR::Value(index)}));
};
const auto minimum = [&](IR::U32 lhs, IR::U32 rhs) {
return IR::U32(entry_ir.Emit(IR::ValueOpcode::UMin32, {lhs, rhs}));
};
const auto subtract_saturate = [&](IR::U32 lhs, IR::U32 rhs) {
return entry_ir.ISub(lhs, minimum(lhs, rhs));
};
const auto local = builtin(IR::StageInputKind::LocalInvocationIndex);
const auto chunk = entry_ir.IMul(builtin(IR::StageInputKind::WorkgroupId, 0),
u32(mesh.primitives_per_group));
const auto vertices =
minimum(subtract_saturate(draw(0), chunk), u32(mesh.vertices_per_group));
const auto primitives = subtract_saturate(vertices, u32(2));
const auto wave = entry_ir.ShiftRightLogical(local, u32(6));
const auto wave_base = entry_ir.BitwiseAnd(local, u32(~63u));
const auto vertex_count = minimum(subtract_saturate(vertices, wave_base), u32(64));
const auto primitive_count = minimum(subtract_saturate(primitives, wave_base), u32(64));
const auto wave_info = entry_ir.BitwiseOr(entry_ir.ShiftLeftLogical(wave, u32(24)),
u32(((total_threads + 63u) / 64u) << 28u));
entry_ir.SetScalarReg(
static_cast<IR::ScalarReg>(3),
entry_ir.BitwiseOr(wave_info, entry_ir.BitwiseOr(entry_ir.ShiftLeftLogical(
primitive_count, u32(8)),
vertex_count)));
// Triangle-strip adjacency addresses the ES records in LDS. Alternating the
// first two indices preserves winding across subgroup boundaries.
const auto parity = entry_ir.BitwiseAnd(entry_ir.IAdd(chunk, local), u32(1));
const auto first = entry_ir.IAdd(local, parity);
const auto second = entry_ir.ISub(entry_ir.IAdd(local, u32(1)), parity);
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(0),
entry_ir.BitwiseOr(entry_ir.ShiftLeftLogical(first, u32(2)),
entry_ir.ShiftLeftLogical(second, u32(18))));
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(1),
entry_ir.ShiftLeftLogical(entry_ir.IAdd(local, u32(2)), u32(2)));
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(5),
entry_ir.IAdd(draw(1), entry_ir.IAdd(chunk, local)));
entry_ir.SetVectorReg(
static_cast<IR::VectorReg>(8),
entry_ir.IAdd(draw(2), builtin(IR::StageInputKind::WorkgroupId, 1)));
} else if (options.stage == ShaderType::Pixel) {
const auto* ps = options.pixel;
if (ps->ps_perspective_center_vgpr != UINT32_MAX) {
@@ -29,7 +29,7 @@ private:
IR::U32 ReadScalarCode(uint32_t code);
IR::U32 ApplyBitSourceModifiers(const Decoder::Operand& operand, IR::U32 value);
IR::Value ReadOperand(const Decoder::Operand& operand, IR::Type type);
IR::U1 ThreadBit(IR::U32 low);
IR::U1 ThreadBit(const std::array<IR::U32, 2>& mask);
void WriteRawU32(const Decoder::Operand& operand, IR::U32 value);
IR::F32 ApplyF32ResultModifiers(const Decoder::Operand& operand, IR::F32 value);
void WriteOperand(const Decoder::Operand& operand, IR::Value value);
@@ -53,8 +53,8 @@ private:
IR::U32 ConditionBit(const Decoder::Operand& operand);
IR::U1 ReadMask(const Decoder::Operand& operand);
IR::U1 ReadMaskValid(const Decoder::Operand& operand);
void WriteMask(const Decoder::Operand& operand, IR::U1 value);
void WriteMask64(const Decoder::Operand& operand, IR::U1 value);
std::array<IR::U32, 2> WriteMask(const Decoder::Operand& operand, IR::U1 value,
bool write_64 = false);
void WriteCompareResult(const Decoder::Operand& operand, IR::U1 value);
IR::MemoryFlags AddMemoryInfo(const IR::MemoryInfo& memory, uint32_t pc);
@@ -228,7 +228,7 @@ private:
void EmitControlNop();
void EmitWaitcnt();
void S_BARRIER();
void S_SENDMSG();
void S_SENDMSG(const Decoder::Instruction& inst);
void S_TTRACEDATA();
void S_INST_PREFETCH();
void S_GETPC_B64(const Decoder::Instruction& inst);