mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
shader: read lane 0 in V_READFIRSTLANE when EXEC is zero instead of an undefined lane
This commit is contained in:
@@ -493,29 +493,24 @@ uint32_t ValueEmitContext::Ballot(IR::Value predicate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ValueEmitContext::FirstLane(uint32_t ballot) {
|
uint32_t ValueEmitContext::FirstLane(uint32_t ballot) {
|
||||||
if (other_half == nullptr) {
|
// An empty ballot (EXEC zero) gives lane 0, which V_READFIRSTLANE reads on hardware. FindILsb
|
||||||
const auto result = state.builder.AllocateId();
|
// returns -1 for zero, unlike BallotFindLSB, whose result is then undefined.
|
||||||
state.builder.AddFunction(spv::OpGroupNonUniformBallotFindLSB, TypeU32(state), result,
|
const auto first = [&](uint32_t word) {
|
||||||
ConstantU32(state, spv::ScopeSubgroup), ballot);
|
const auto bits = state.builder.AllocateId();
|
||||||
return result;
|
state.builder.AddFunction(spv::OpCompositeExtract, TypeU32(state), bits, ballot, word);
|
||||||
|
return std::pair {bits, EmitGlsl<GLSLstd450FindILsb, IR::Type::U32>(state, bits)};
|
||||||
|
};
|
||||||
|
const auto [low, low_first] = first(0);
|
||||||
|
if (state.program.wave_size == 32u) {
|
||||||
|
return EmitGlsl<GLSLstd450SMax, IR::Type::U32>(state, low_first, ConstantU32(state, 0));
|
||||||
}
|
}
|
||||||
const auto low = state.builder.AllocateId();
|
const auto high_first = first(1).second;
|
||||||
const auto high = state.builder.AllocateId();
|
const auto high_lane = EmitGlsl<GLSLstd450SMax, IR::Type::U32>(
|
||||||
const auto low_first = state.builder.AllocateId();
|
state, EmitBinaryU32(state, spv::OpBitwiseOr, high_first, ConstantU32(state, 32)),
|
||||||
const auto high_first = state.builder.AllocateId();
|
ConstantU32(state, 0));
|
||||||
const auto low_active = state.builder.AllocateId();
|
return Select(state, TypeU32(state),
|
||||||
const auto result = state.builder.AllocateId();
|
Binary(state, spv::OpINotEqual, TypeBool(state), low, ConstantU32(state, 0)),
|
||||||
state.builder.AddFunction(spv::OpCompositeExtract, TypeU32(state), low, ballot, 0);
|
low_first, high_lane);
|
||||||
state.builder.AddFunction(spv::OpCompositeExtract, TypeU32(state), high, ballot, 1);
|
|
||||||
state.builder.AddFunction(spv::OpExtInst, TypeU32(state), low_first, GlslStd450(state),
|
|
||||||
GLSLstd450FindILsb, low);
|
|
||||||
state.builder.AddFunction(spv::OpExtInst, TypeU32(state), high_first, GlslStd450(state),
|
|
||||||
GLSLstd450FindILsb, high);
|
|
||||||
state.builder.AddFunction(spv::OpINotEqual, TypeBool(state), low_active, low,
|
|
||||||
ConstantU32(state, 0));
|
|
||||||
state.builder.AddFunction(spv::OpSelect, TypeU32(state), result, low_active, low_first,
|
|
||||||
EmitAddU32(state, high_first, ConstantU32(state, 32)));
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ValueEmitContext::Shuffle(const IR::Inst& inst, size_t index, uint32_t lane) {
|
uint32_t ValueEmitContext::Shuffle(const IR::Inst& inst, size_t index, uint32_t lane) {
|
||||||
|
|||||||
@@ -23006,6 +23006,38 @@ TestCase ScalarZeroBitOps() {
|
|||||||
return test;
|
return test;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// V_READFIRSTLANE reads the first EXEC lane, or lane 0 when EXEC is zero; v1 = 100 + lane.
|
||||||
|
TestCase VectorReadFirstLaneExecMasks(u32 wave_size) {
|
||||||
|
using O = ShaderOpcode;
|
||||||
|
TestCase test;
|
||||||
|
test.name = wave_size == 32 ? "VectorReadFirstLaneExecMasksWave32"
|
||||||
|
: "VectorReadFirstLaneExecMasksWave64";
|
||||||
|
auto &code = test.code;
|
||||||
|
AppendVMovU32(&code, 1, 100);
|
||||||
|
code.push_back(EncodeVop2(0x25, 1, Vgpr(0), 1));
|
||||||
|
code.push_back(EncodeSop1(0x04, 10, 126)); // s_mov_b64 s[10:11], exec
|
||||||
|
const uint64_t masks[] = {0, 1ull << 5, 0x80000000ull, 1ull << 32, 1ull << 63, 0xffff000000000000ull};
|
||||||
|
for (const uint64_t mask : masks) {
|
||||||
|
if (wave_size == 32 && mask > UINT32_MAX) continue;
|
||||||
|
AppendSMovLiteral(&code, 126, u32(mask));
|
||||||
|
if (wave_size == 64) AppendSMovLiteral(&code, 127, u32(mask >> 32));
|
||||||
|
code.push_back(EncodeVop1(0x02, 20, Vgpr(1)));
|
||||||
|
code.push_back(EncodeSop1(0x04, 126, 10));
|
||||||
|
AppendStoreSgpr(&code, 20, u32(test.expected.size()));
|
||||||
|
test.expected.push_back(100 + (mask == 0 ? 0 : std::countr_zero(mask)));
|
||||||
|
}
|
||||||
|
AppendEnd(&code);
|
||||||
|
test.initial.resize(test.expected.size());
|
||||||
|
test.opcodes = {O::V_MOV_B32, O::V_ADD_NC_U32, O::S_MOV_B32, O::S_MOV_B64,
|
||||||
|
O::V_READFIRSTLANE_B32, O::BUFFER_STORE_DWORD, O::S_ENDPGM};
|
||||||
|
test.compute_info.wave_size = wave_size;
|
||||||
|
test.compute_info.threads_num[0] = wave_size;
|
||||||
|
test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1;
|
||||||
|
test.compute_info.thread_ids_num = 1;
|
||||||
|
test.has_compute_info = true;
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
TestCase ScalarSextMasks(u32 bits, u32 wave_size) {
|
TestCase ScalarSextMasks(u32 bits, u32 wave_size) {
|
||||||
using O = ShaderOpcode;
|
using O = ShaderOpcode;
|
||||||
TestCase test;
|
TestCase test;
|
||||||
@@ -43094,6 +43126,8 @@ std::vector<TestCase> MakeCases() {
|
|||||||
cases.push_back(VectorDppRowShare(64));
|
cases.push_back(VectorDppRowShare(64));
|
||||||
cases.push_back(VectorDppMirrorAndRotate(32));
|
cases.push_back(VectorDppMirrorAndRotate(32));
|
||||||
cases.push_back(VectorDppMirrorAndRotate(64));
|
cases.push_back(VectorDppMirrorAndRotate(64));
|
||||||
|
cases.push_back(VectorReadFirstLaneExecMasks(32));
|
||||||
|
cases.push_back(VectorReadFirstLaneExecMasks(64));
|
||||||
cases.push_back(ComputeWave64Minimum(false));
|
cases.push_back(ComputeWave64Minimum(false));
|
||||||
cases.push_back(ComputeWave64Minimum(true));
|
cases.push_back(ComputeWave64Minimum(true));
|
||||||
AddCase(VectorDppBankMaskPreservesDestination);
|
AddCase(VectorDppBankMaskPreservesDestination);
|
||||||
@@ -49186,6 +49220,8 @@ int main(int argc, char **argv) {
|
|||||||
RunCase(&vulkan, VectorDppRowShare(64));
|
RunCase(&vulkan, VectorDppRowShare(64));
|
||||||
RunCase(&vulkan, VectorDppMirrorAndRotate(32));
|
RunCase(&vulkan, VectorDppMirrorAndRotate(32));
|
||||||
RunCase(&vulkan, VectorDppMirrorAndRotate(64));
|
RunCase(&vulkan, VectorDppMirrorAndRotate(64));
|
||||||
|
RunCase(&vulkan, VectorReadFirstLaneExecMasks(32));
|
||||||
|
RunCase(&vulkan, VectorReadFirstLaneExecMasks(64));
|
||||||
RunCase(&vulkan, VectorDppBankMaskPreservesDestination());
|
RunCase(&vulkan, VectorDppBankMaskPreservesDestination());
|
||||||
RunCase(&vulkan, VectorDppBoundsControlZeroPreservesDestination());
|
RunCase(&vulkan, VectorDppBoundsControlZeroPreservesDestination());
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user