shader: keep the old V_PERMLANE16 destination for inactive sources without BOUND_CTRL

This commit is contained in:
nmzik
2026-10-25 01:20:36 +02:00
parent 5d8381e0f3
commit 67c89e0915
5 changed files with 74 additions and 60 deletions
@@ -114,6 +114,18 @@ uint32_t EmitBuiltinU32(EmitterState& state, IR::StageInputKind kind, uint32_t c
return EmitInputComponentU32(state, kind, component);
}
// Whether EXEC enables a source lane within the caller's 32-lane half.
uint32_t EmitSourceLaneActive(ValueEmitContext& ctx, uint32_t exec, uint32_t lane) {
auto& state = ctx.state;
const auto scope = ConstantU32(state, spv::ScopeSubgroup);
const auto ballot = EmitNative<spv::OpGroupNonUniformBallot, IR::Type::U32x4>(state, scope, exec);
if (ctx.half != 0) {
lane = EmitAndConstant(state, lane, 31);
}
return EmitNative<spv::OpGroupNonUniformBallotBitExtract, IR::Type::U1>(state, scope, ballot,
lane);
}
uint32_t EmitDppWriteCondition(ValueEmitContext& ctx, const IR::DppMoveFlags& flags,
uint32_t exec) {
auto& state = ctx.state;
@@ -852,14 +864,11 @@ uint32_t EmitPermlane16U32(ValueEmitContext& ctx, const IR::Inst& inst) {
ConstantU32(state, 15));
state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, row_value, index);
const auto shuffled = ctx.Shuffle(inst, 0, target);
uint32_t result = shuffled;
if (!flags.fetch_inactive) {
const auto source_exec = ctx.Shuffle(inst, 3, target);
result = state.builder.AllocateId();
state.builder.AddFunction(spv::OpSelect, TypeU32(state), result, source_exec, shuffled,
ConstantU32(state, 0));
if (flags.fetch_inactive) {
return shuffled;
}
return result;
return Select(state, TypeU32(state), EmitSourceLaneActive(ctx, ctx.Arg(inst, 3), target),
shuffled, ctx.Arg(inst, 4));
}
uint32_t EmitGetAttribute(ValueEmitContext& ctx, const IR::Inst& inst) {
@@ -465,9 +465,13 @@ void Translator::V_PERMLANE16_B32(const Decoder::Instruction& inst, bool x16) {
.fetch_inactive = inst.dst.op_sel,
.bound_control = inst.dst.op_sel_hi,
};
const auto result =
ir.Emit(IR::ValueOpcode::Permlane16U32,
{ReadU32(inst.src0), ReadU32(inst.src1), ReadU32(inst.src2), ir.GetExec()}, flags);
// An EXEC-disabled source lane without FI disables the write unless BOUND_CTRL reads zero.
const auto inactive = flags.fetch_inactive || flags.bound_control
? IR::U32(IR::Value(0u))
: ir.GetVectorReg(static_cast<IR::VectorReg>(inst.dst.reg));
const auto result = ir.Emit(
IR::ValueOpcode::Permlane16U32,
{ReadU32(inst.src0), ReadU32(inst.src1), ReadU32(inst.src2), ir.GetExec(), inactive}, flags);
auto dst = DestinationOperand(inst);
dst.op_sel = false;
dst.op_sel_hi = false;
@@ -223,7 +223,7 @@ VALUE_OPCODE(ReadFirstLane, U32, U32, U1)
VALUE_OPCODE(ReadLane, U32, U32, U32)
VALUE_OPCODE(SubgroupUMinU32, U32, U32)
VALUE_OPCODE(WriteLane, U32, U32, U32, U32)
VALUE_OPCODE(Permlane16U32, U32, U32, U32, U32, U1)
VALUE_OPCODE(Permlane16U32, U32, U32, U32, U32, U1, U32)
VALUE_OPCODE(PermuteU32, U32, U32, U32, U1)
VALUE_OPCODE(BpermuteU32, U32, U32, U32, U1)