mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 19:07:01 +02:00
test(shader): establish SPIR-V size baselines
This commit is contained in:
@@ -184,6 +184,128 @@ uint32_t SpirvInstructionOpcodeCount(const std::vector<uint32_t> &binary,
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SpirvMetrics {
|
||||||
|
size_t words = 0;
|
||||||
|
uint32_t instructions = 0;
|
||||||
|
uint32_t type_images = 0;
|
||||||
|
uint32_t variables = 0;
|
||||||
|
uint32_t loads = 0;
|
||||||
|
uint32_t stores = 0;
|
||||||
|
uint32_t array_lengths = 0;
|
||||||
|
uint32_t phis = 0;
|
||||||
|
uint32_t labels = 0;
|
||||||
|
uint32_t loop_merges = 0;
|
||||||
|
uint32_t selection_merges = 0;
|
||||||
|
uint32_t branches = 0;
|
||||||
|
uint32_t conditional_branches = 0;
|
||||||
|
uint32_t switches = 0;
|
||||||
|
uint32_t ballots = 0;
|
||||||
|
uint32_t quad_broadcasts = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
SpirvMetrics MeasureSpirv(const std::vector<uint32_t> &binary) {
|
||||||
|
SpirvMetrics metrics{};
|
||||||
|
metrics.words = binary.size();
|
||||||
|
for (size_t i = 5; i < binary.size();) {
|
||||||
|
const uint32_t word = binary[i];
|
||||||
|
const uint32_t opcode = word & 0xffffu;
|
||||||
|
const uint32_t word_count = word >> 16u;
|
||||||
|
if (word_count == 0 || i + word_count > binary.size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
metrics.instructions++;
|
||||||
|
switch (opcode) {
|
||||||
|
case 25u: // OpTypeImage
|
||||||
|
metrics.type_images++;
|
||||||
|
break;
|
||||||
|
case 59u: // OpVariable
|
||||||
|
metrics.variables++;
|
||||||
|
break;
|
||||||
|
case 61u: // OpLoad
|
||||||
|
metrics.loads++;
|
||||||
|
break;
|
||||||
|
case 62u: // OpStore
|
||||||
|
metrics.stores++;
|
||||||
|
break;
|
||||||
|
case 68u: // OpArrayLength
|
||||||
|
metrics.array_lengths++;
|
||||||
|
break;
|
||||||
|
case 245u: // OpPhi
|
||||||
|
metrics.phis++;
|
||||||
|
break;
|
||||||
|
case 246u: // OpLoopMerge
|
||||||
|
metrics.loop_merges++;
|
||||||
|
break;
|
||||||
|
case 247u: // OpSelectionMerge
|
||||||
|
metrics.selection_merges++;
|
||||||
|
break;
|
||||||
|
case 248u: // OpLabel
|
||||||
|
metrics.labels++;
|
||||||
|
break;
|
||||||
|
case 249u: // OpBranch
|
||||||
|
metrics.branches++;
|
||||||
|
break;
|
||||||
|
case 250u: // OpBranchConditional
|
||||||
|
metrics.conditional_branches++;
|
||||||
|
break;
|
||||||
|
case 251u: // OpSwitch
|
||||||
|
metrics.switches++;
|
||||||
|
break;
|
||||||
|
case 339u: // OpGroupNonUniformBallot
|
||||||
|
metrics.ballots++;
|
||||||
|
break;
|
||||||
|
case 365u: // OpGroupNonUniformQuadBroadcast
|
||||||
|
metrics.quad_broadcasts++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += word_count;
|
||||||
|
}
|
||||||
|
return metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckSpirvBudget(const char *name, const std::vector<uint32_t> &binary,
|
||||||
|
const SpirvMetrics &budget) {
|
||||||
|
const auto actual = MeasureSpirv(binary);
|
||||||
|
const bool within =
|
||||||
|
actual.words <= budget.words &&
|
||||||
|
actual.instructions <= budget.instructions &&
|
||||||
|
actual.type_images <= budget.type_images &&
|
||||||
|
actual.variables <= budget.variables && actual.loads <= budget.loads &&
|
||||||
|
actual.stores <= budget.stores &&
|
||||||
|
actual.array_lengths <= budget.array_lengths &&
|
||||||
|
actual.phis <= budget.phis && actual.labels <= budget.labels &&
|
||||||
|
actual.loop_merges <= budget.loop_merges &&
|
||||||
|
actual.selection_merges <= budget.selection_merges &&
|
||||||
|
actual.branches <= budget.branches &&
|
||||||
|
actual.conditional_branches <= budget.conditional_branches &&
|
||||||
|
actual.switches <= budget.switches && actual.ballots <= budget.ballots &&
|
||||||
|
actual.quad_broadcasts <= budget.quad_broadcasts;
|
||||||
|
if (!within) {
|
||||||
|
std::fprintf(
|
||||||
|
stderr,
|
||||||
|
"SPIR-V budget %s exceeded\n"
|
||||||
|
" actual: words=%zu insts=%u images=%u vars=%u loads=%u stores=%u "
|
||||||
|
"lengths=%u phis=%u labels=%u loops=%u selections=%u branches=%u "
|
||||||
|
"cond=%u switches=%u ballots=%u quad=%u\n"
|
||||||
|
" budget: words=%zu insts=%u images=%u vars=%u loads=%u stores=%u "
|
||||||
|
"lengths=%u phis=%u labels=%u loops=%u selections=%u branches=%u "
|
||||||
|
"cond=%u switches=%u ballots=%u quad=%u\n",
|
||||||
|
name, actual.words, actual.instructions, actual.type_images,
|
||||||
|
actual.variables, actual.loads, actual.stores, actual.array_lengths,
|
||||||
|
actual.phis, actual.labels, actual.loop_merges,
|
||||||
|
actual.selection_merges, actual.branches, actual.conditional_branches,
|
||||||
|
actual.switches, actual.ballots, actual.quad_broadcasts, budget.words,
|
||||||
|
budget.instructions, budget.type_images, budget.variables, budget.loads,
|
||||||
|
budget.stores, budget.array_lengths, budget.phis, budget.labels,
|
||||||
|
budget.loop_merges, budget.selection_merges, budget.branches,
|
||||||
|
budget.conditional_branches, budget.switches, budget.ballots,
|
||||||
|
budget.quad_broadcasts);
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t SpirvArrayLengthCount(const std::vector<uint32_t> &binary,
|
uint32_t SpirvArrayLengthCount(const std::vector<uint32_t> &binary,
|
||||||
uint32_t requested_length) {
|
uint32_t requested_length) {
|
||||||
std::vector<uint32_t> length_ids;
|
std::vector<uint32_t> length_ids;
|
||||||
@@ -9447,6 +9569,134 @@ void TestNewShaderRecompilerFlatAddressProvenanceBoundaries() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestNewShaderRecompilerSpirvSizeBaselines() {
|
||||||
|
const auto compile = [](const char *name, std::span<const uint32_t> shader,
|
||||||
|
const SpirvMetrics &budget,
|
||||||
|
ShaderType stage = ShaderType::Compute,
|
||||||
|
ShaderLaneMaskMode lane_mask_mode =
|
||||||
|
ShaderLaneMaskMode::NativeWave) {
|
||||||
|
auto options = MakeCompileOptions(stage);
|
||||||
|
options.dump_ir = true;
|
||||||
|
options.lane_mask_mode = lane_mask_mode;
|
||||||
|
|
||||||
|
ShaderRecompiler::CompileResult result;
|
||||||
|
std::string error;
|
||||||
|
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error),
|
||||||
|
error.c_str());
|
||||||
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
|
CheckSpirvBudget(name, result.spirv, budget);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32_t empty[] = {EncodeSopp(0x01)};
|
||||||
|
compile("empty", empty,
|
||||||
|
{.words = 640, .instructions = 140, .type_images = 24,
|
||||||
|
.variables = 1, .labels = 4, .branches = 3});
|
||||||
|
|
||||||
|
const uint32_t structured_phi[] = {
|
||||||
|
EncodeSMovB32(0, 128), // s0 = 0
|
||||||
|
EncodeSopc(0x0a, 0, 129), // loop: s_cmp_lt_u32 s0, 1
|
||||||
|
EncodeSopp(0x04, 2), // break when scc == 0
|
||||||
|
EncodeSop2(0x00, 0, 0, 129), // s_add_u32 s0, s0, 1
|
||||||
|
EncodeSopp(0x02, 0xfffcu), // continue/backedge
|
||||||
|
EncodeSopp(0x01),
|
||||||
|
};
|
||||||
|
const auto structured_result =
|
||||||
|
compile("structured-phi", structured_phi,
|
||||||
|
{.words = 705,
|
||||||
|
.instructions = 158,
|
||||||
|
.type_images = 24,
|
||||||
|
.variables = 2,
|
||||||
|
.loads = 1,
|
||||||
|
.stores = 2,
|
||||||
|
.phis = 1,
|
||||||
|
.labels = 7,
|
||||||
|
.loop_merges = 1,
|
||||||
|
.branches = 5,
|
||||||
|
.conditional_branches = 1});
|
||||||
|
Check(Common::ContainsStr(structured_result.ir_dump, "Phi"),
|
||||||
|
"structured Phi size fixture no longer contains an IR Phi");
|
||||||
|
|
||||||
|
const uint32_t wide_buffer[] = {
|
||||||
|
EncodeMubuf0(0x0e, 0),
|
||||||
|
EncodeMubuf1(0, 0, 1), // buffer_load_dwordx4 v[0:3]
|
||||||
|
EncodeMubuf0(0x1e, 16),
|
||||||
|
EncodeMubuf1(0, 0, 1), // buffer_store_dwordx4 v[0:3]
|
||||||
|
EncodeSopp(0x01),
|
||||||
|
};
|
||||||
|
const auto wide_result =
|
||||||
|
compile("wide-buffer", wide_buffer,
|
||||||
|
{.words = 2549,
|
||||||
|
.instructions = 583,
|
||||||
|
.type_images = 24,
|
||||||
|
.variables = 3,
|
||||||
|
.loads = 9,
|
||||||
|
.stores = 4,
|
||||||
|
.array_lengths = 8,
|
||||||
|
.phis = 8,
|
||||||
|
.labels = 52,
|
||||||
|
.selection_merges = 16,
|
||||||
|
.branches = 35,
|
||||||
|
.conditional_branches = 16});
|
||||||
|
Check(Common::ContainsStr(wide_result.decoded_dump,
|
||||||
|
"BUFFER_LOAD_DWORDX4"),
|
||||||
|
"wide buffer size fixture no longer decodes its x4 load");
|
||||||
|
Check(Common::ContainsStr(wide_result.decoded_dump,
|
||||||
|
"BUFFER_STORE_DWORDX4"),
|
||||||
|
"wide buffer size fixture no longer decodes its x4 store");
|
||||||
|
|
||||||
|
const uint32_t wqm[] = {
|
||||||
|
EncodeVopc(0xc1, 5 + 256, 8), // v_cmp_lt_u32 vcc, v5, v8
|
||||||
|
EncodeSop1(0x0a, 2, 106), // s_wqm_b64 s[2:3], vcc
|
||||||
|
EncodeVop1(0x01, 0, 2), // v_mov_b32 v0, s2
|
||||||
|
EncodeExp0(0x0c, 0x1),
|
||||||
|
EncodeExp1(0, 0, 0, 0), // POS0.x
|
||||||
|
EncodeSopp(0x01),
|
||||||
|
};
|
||||||
|
const auto wqm_result =
|
||||||
|
compile("wqm", wqm,
|
||||||
|
{.words = 1221,
|
||||||
|
.instructions = 260,
|
||||||
|
.type_images = 24,
|
||||||
|
.variables = 4,
|
||||||
|
.loads = 3,
|
||||||
|
.stores = 1,
|
||||||
|
.labels = 6,
|
||||||
|
.selection_merges = 1,
|
||||||
|
.branches = 4,
|
||||||
|
.conditional_branches = 1,
|
||||||
|
.ballots = 1,
|
||||||
|
.quad_broadcasts = 4},
|
||||||
|
ShaderType::Vertex, ShaderLaneMaskMode::PerInvocation);
|
||||||
|
Check(Common::ContainsStr(wqm_result.ir_dump, "WqmMask"),
|
||||||
|
"WQM size fixture no longer reaches per-invocation WqmMask IR");
|
||||||
|
|
||||||
|
const uint32_t dispatcher[] = {
|
||||||
|
EncodeSopp(0x05, 2), // entry -> B, fallthrough A
|
||||||
|
EncodeSopp(0x02, 0), // A -> C
|
||||||
|
EncodeSopp(0x05, 0xfffeu), // C -> A, fallthrough B
|
||||||
|
EncodeSopp(0x02, 0xfffeu), // B -> C
|
||||||
|
EncodeSopp(0x01),
|
||||||
|
};
|
||||||
|
const auto dispatcher_result =
|
||||||
|
compile("dispatcher", dispatcher,
|
||||||
|
{.words = 822,
|
||||||
|
.instructions = 195,
|
||||||
|
.type_images = 24,
|
||||||
|
.variables = 5,
|
||||||
|
.loads = 4,
|
||||||
|
.stores = 14,
|
||||||
|
.labels = 14,
|
||||||
|
.loop_merges = 1,
|
||||||
|
.selection_merges = 1,
|
||||||
|
.branches = 11,
|
||||||
|
.conditional_branches = 1,
|
||||||
|
.switches = 1});
|
||||||
|
Check(dispatcher_result.program.dispatcher_fallback &&
|
||||||
|
SpirvInstructionOpcodeCount(dispatcher_result.spirv, 251u) == 1u,
|
||||||
|
"dispatcher size fixture no longer exercises whole-function dispatch");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace Libs::Graphics
|
} // namespace Libs::Graphics
|
||||||
|
|
||||||
@@ -9458,6 +9708,7 @@ int main() {
|
|||||||
TestNativeShaderResourceDependencies();
|
TestNativeShaderResourceDependencies();
|
||||||
TestNormalizedImageContracts();
|
TestNormalizedImageContracts();
|
||||||
TestSpirvRequirementsAnalysis();
|
TestSpirvRequirementsAnalysis();
|
||||||
|
TestNewShaderRecompilerSpirvSizeBaselines();
|
||||||
TestNativeSubgroupPolicy();
|
TestNativeSubgroupPolicy();
|
||||||
TestNewShaderRecompilerSMovB32();
|
TestNewShaderRecompilerSMovB32();
|
||||||
TestNewShaderRecompilerCapturedVop1SdwaByteConvert();
|
TestNewShaderRecompilerCapturedVop1SdwaByteConvert();
|
||||||
|
|||||||
Reference in New Issue
Block a user