shader: compute DPP permute, rotate, mirror and share lanes with bit-field and XOR ops

This commit is contained in:
nmzik
2026-10-22 00:40:07 +02:00
parent 6123419437
commit 09a9654458
2 changed files with 57 additions and 59 deletions
@@ -4,25 +4,16 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
DppTargetLane EmitDppPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control,
uint32_t lane_bits) {
const auto lane_mask = (1u << lane_bits) - 1u;
const auto group_base = state.builder.AllocateId();
const auto lane = state.builder.AllocateId();
const auto shift = state.builder.AllocateId();
const auto selected0 = state.builder.AllocateId();
const auto selected = state.builder.AllocateId();
const auto target = state.builder.AllocateId();
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), group_base, subid,
ConstantU32(state, ~lane_mask));
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), lane, subid,
ConstantU32(state, lane_mask));
state.builder.AddFunction(spv::OpIMul, TypeU32(state), shift, lane,
ConstantU32(state, lane_bits));
state.builder.AddFunction(spv::OpShiftRightLogical, TypeU32(state), selected0,
ConstantU32(state, control), shift);
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), selected, selected0,
ConstantU32(state, lane_mask));
state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, group_base, selected);
return {target, ConstantBool(state, true)};
// Each lane of a group takes its lane_bits-wide source index from control.
const auto lane = Binary(state, spv::OpBitwiseAnd, TypeU32(state), subid,
ConstantU32(state, (1u << lane_bits) - 1u));
const auto selected = EmitNative<spv::OpBitFieldUExtract, IR::Type::U32>(
state, ConstantU32(state, control),
Binary(state, spv::OpIMul, TypeU32(state), lane, ConstantU32(state, lane_bits)),
ConstantU32(state, lane_bits));
return {EmitNative<spv::OpBitFieldInsert, IR::Type::U32>(
state, subid, selected, ConstantU32(state, 0), ConstantU32(state, lane_bits)),
ConstantBool(state, true)};
}
DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uint32_t amount,
@@ -53,42 +44,19 @@ DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uin
DppTargetLane EmitDppRowRotateRightTargetLane(EmitterState& state, uint32_t subid,
uint32_t amount) {
const auto row = state.builder.AllocateId();
const auto lane = state.builder.AllocateId();
const auto in_high = state.builder.AllocateId();
const auto minus = state.builder.AllocateId();
const auto plus = state.builder.AllocateId();
const auto selected = state.builder.AllocateId();
const auto target = state.builder.AllocateId();
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), row, subid,
ConstantU32(state, 0xfffffff0u));
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), lane, subid,
ConstantU32(state, 15));
state.builder.AddFunction(spv::OpUGreaterThanEqual, TypeBool(state), in_high, lane,
ConstantU32(state, amount));
state.builder.AddFunction(spv::OpISub, TypeU32(state), minus, lane, ConstantU32(state, amount));
state.builder.AddFunction(spv::OpIAdd, TypeU32(state), plus, lane,
ConstantU32(state, 16u - amount));
state.builder.AddFunction(spv::OpSelect, TypeU32(state), selected, in_high, minus, plus);
state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, row, selected);
return {target, ConstantBool(state, true)};
// Rotation wraps within the row, so only the low four lane bits change.
const auto rotated = Binary(state, spv::OpISub, TypeU32(state), subid, ConstantU32(state, amount));
return {EmitNative<spv::OpBitFieldInsert, IR::Type::U32>(state, subid, rotated,
ConstantU32(state, 0),
ConstantU32(state, 4)),
ConstantBool(state, true)};
}
DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool half_row) {
const auto base_mask = half_row ? 0xfffffff8u : 0xfffffff0u;
const auto lane_mask = half_row ? 7u : 15u;
const auto base = state.builder.AllocateId();
const auto lane = state.builder.AllocateId();
const auto mirrored = state.builder.AllocateId();
const auto target = state.builder.AllocateId();
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), base, subid,
ConstantU32(state, base_mask));
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), lane, subid,
ConstantU32(state, lane_mask));
state.builder.AddFunction(spv::OpISub, TypeU32(state), mirrored, ConstantU32(state, lane_mask),
lane);
state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, base, mirrored);
return {target, ConstantBool(state, true)};
// Mirroring lane i of a row or half row reads lane (size - 1 - i), which flips its low bits.
return {Binary(state, spv::OpBitwiseXor, TypeU32(state), subid,
ConstantU32(state, half_row ? 7u : 15u)),
ConstantBool(state, true)};
}
DppTargetLane EmitDppTargetLane(EmitterState& state, const IR::DppMoveFlags& flags) {
@@ -117,13 +85,10 @@ DppTargetLane EmitDppTargetLane(EmitterState& state, const IR::DppMoveFlags& fla
}
// row_share:N reads lane N of the caller's row of 16.
if (control >= 0x150u && control <= 0x15fu) {
const auto row = state.builder.AllocateId();
const auto target = state.builder.AllocateId();
state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), row, subid,
ConstantU32(state, 0xfffffff0u));
state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, row,
ConstantU32(state, control & 0xfu));
return {target, ConstantBool(state, true)};
return {EmitNative<spv::OpBitFieldInsert, IR::Type::U32>(
state, subid, ConstantU32(state, control & 0xfu), ConstantU32(state, 0),
ConstantU32(state, 4)),
ConstantBool(state, true)};
}
if (control >= 0x160u && control <= 0x16fu) {
const auto target = state.builder.AllocateId();