mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
shader: use Khronos definitions and simplify compiler ownership
This commit is contained in:
+234
-50
@@ -154,7 +154,7 @@ void CompilePixelRuntime(const ShaderParams ¶ms,
|
||||
auto options = MakeCompileOptions(ShaderType::Pixel);
|
||||
options.shader_hash = params.hash;
|
||||
options.user_data = params.user_data;
|
||||
options.scratch_dwords = input_info.scratch_size_dwords;
|
||||
|
||||
options.input_info.pixel = &input_info;
|
||||
auto result = RecompileForTest(params.code, options);
|
||||
static std::deque<ShaderRecompiler::IR::CompiledShaderInfo> programs;
|
||||
@@ -1260,8 +1260,8 @@ void TestSpirvRequirementsAnalysis() {
|
||||
block->AppendNewInst(ValueOpcode::SetAttribute, {Value(0u), Value(true)});
|
||||
export_value.SetFlags(ExportFlags{.index = 0});
|
||||
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program);
|
||||
const auto requirements = *program.spirv_requirements;
|
||||
const auto requirements =
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(program);
|
||||
Check(requirements.subgroup_ballot && requirements.subgroup_shuffle &&
|
||||
requirements.subgroup_local_invocation_id &&
|
||||
requirements.compute_derivatives &&
|
||||
@@ -1270,8 +1270,8 @@ void TestSpirvRequirementsAnalysis() {
|
||||
"consolidated SPIR-V requirements missed an IR dependency");
|
||||
|
||||
program.stage = ShaderType::Compute;
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program);
|
||||
const auto compute_requirements = *program.spirv_requirements;
|
||||
const auto compute_requirements =
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(program);
|
||||
Check(!compute_requirements.function_lds &&
|
||||
!compute_requirements.pixel_valid_mask,
|
||||
"stage-specific SPIR-V requirements leaked into compute");
|
||||
@@ -1281,14 +1281,14 @@ void TestSpirvRequirementsAnalysis() {
|
||||
[&] {
|
||||
program.stage = ShaderType::Pixel;
|
||||
shared.SetFlags(MemoryFlags{.index = 1});
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program);
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(program);
|
||||
},
|
||||
"invalid shared-memory requirement metadata did not terminate analysis");
|
||||
ExpectFatal(
|
||||
[&] {
|
||||
program.stage = ShaderType::Pixel;
|
||||
export_value.SetFlags(ExportFlags{.index = 1});
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program);
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(program);
|
||||
},
|
||||
"invalid export requirement metadata did not terminate analysis");
|
||||
#endif
|
||||
@@ -1309,8 +1309,8 @@ void TestSpirvRequirementsAnalysis() {
|
||||
ValueOpcode::LoadBufferU32,
|
||||
{Value(&buffer), Value(0u), Value(0u), Value(0u), Value(true)});
|
||||
load.SetFlags(MemoryFlags{.index = 0});
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(add_tid);
|
||||
const auto add_tid_requirements = *add_tid.spirv_requirements;
|
||||
const auto add_tid_requirements =
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(add_tid);
|
||||
Check(add_tid_requirements.subgroup_local_invocation_id &&
|
||||
!add_tid_requirements.subgroup_ballot &&
|
||||
!add_tid_requirements.subgroup_shuffle,
|
||||
@@ -1320,20 +1320,20 @@ void TestSpirvRequirementsAnalysis() {
|
||||
ExpectFatal(
|
||||
[&] {
|
||||
add_tid.memory_info[0].resource = 1;
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(add_tid);
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(add_tid);
|
||||
},
|
||||
"invalid buffer requirement metadata did not terminate analysis");
|
||||
ExpectFatal(
|
||||
[&] {
|
||||
add_tid.stage = ShaderType::Vertex;
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(add_tid);
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(add_tid);
|
||||
},
|
||||
"graphics buffer ADD_TID did not terminate requirements analysis");
|
||||
#endif
|
||||
|
||||
Program empty;
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(empty);
|
||||
const auto empty_requirements = *empty.spirv_requirements;
|
||||
const auto empty_requirements =
|
||||
ShaderRecompiler::Spirv::Emitter::AnalyzeProgramRequirements(empty);
|
||||
Check(!empty_requirements.subgroup_ballot &&
|
||||
!empty_requirements.subgroup_shuffle &&
|
||||
!empty_requirements.subgroup_local_invocation_id &&
|
||||
@@ -4383,7 +4383,7 @@ void TestNewShaderRecompilerCapturedVopcSdwaCmpxClass() {
|
||||
Frontend::TranslateOptions translate_options{};
|
||||
translate_options.stage = ShaderType::Pixel;
|
||||
translate_options.wave_size = 64u;
|
||||
translate_options.pixel = &pixel;
|
||||
translate_options.input_info.pixel = &pixel;
|
||||
ShaderRecompiler::Decoder::DecodeProgram(shader, program);
|
||||
graph = ShaderRecompiler::CFG::BuildGraph(program);
|
||||
ir = ShaderRecompiler::Frontend::TranslateProgram(program, graph,
|
||||
@@ -4431,7 +4431,7 @@ void TestNewShaderRecompilerIrLookupMissFailsExplicitly() {
|
||||
Frontend::TranslateOptions options{};
|
||||
options.stage = ShaderType::Compute;
|
||||
options.wave_size = 64u;
|
||||
options.compute = &compute;
|
||||
options.input_info.compute = &compute;
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
store.pc = 0u;
|
||||
@@ -4807,7 +4807,7 @@ void TestImageAddressOperands() {
|
||||
ShaderComputeInputInfo compute = RegressionComputeInputInfo();
|
||||
Frontend::TranslateOptions options{};
|
||||
options.stage = ShaderType::Compute;
|
||||
options.compute = &compute;
|
||||
options.input_info.compute = &compute;
|
||||
auto program = Frontend::TranslateProgram(decoded, CFG::BuildGraph(decoded), options);
|
||||
IR::RewriteToSsa(program.blocks);
|
||||
IR::ConstantPropagationPass(program.blocks);
|
||||
@@ -6211,7 +6211,7 @@ void TestNewShaderRecompilerNativeWideScalarMemoryIr() {
|
||||
ShaderRecompiler::Frontend::TranslateOptions translate_options{};
|
||||
translate_options.stage = ShaderType::Compute;
|
||||
translate_options.wave_size = 64u;
|
||||
translate_options.compute = &compute;
|
||||
translate_options.input_info.compute = &compute;
|
||||
ShaderRecompiler::Decoder::DecodeProgram(shader, decoded);
|
||||
graph = ShaderRecompiler::CFG::BuildGraph(decoded);
|
||||
ir = ShaderRecompiler::Frontend::TranslateProgram(decoded, graph,
|
||||
@@ -6909,7 +6909,7 @@ void TestNewShaderRecompilerDsReadWrite2Translation() {
|
||||
ShaderRecompiler::Frontend::TranslateOptions translate_options{};
|
||||
translate_options.stage = ShaderType::Compute;
|
||||
translate_options.wave_size = 64u;
|
||||
translate_options.compute = &compute;
|
||||
translate_options.input_info.compute = &compute;
|
||||
ShaderRecompiler::Decoder::DecodeProgram(std::span{shader}, decoded);
|
||||
graph = ShaderRecompiler::CFG::BuildGraph(decoded);
|
||||
typed = ShaderRecompiler::Frontend::TranslateProgram(decoded, graph,
|
||||
@@ -7057,7 +7057,7 @@ void TestNewShaderRecompilerDsWideAndAtomicTranslation() {
|
||||
ShaderRecompiler::Frontend::TranslateOptions translate_options{};
|
||||
translate_options.stage = ShaderType::Compute;
|
||||
translate_options.wave_size = 64u;
|
||||
translate_options.compute = &compute;
|
||||
translate_options.input_info.compute = &compute;
|
||||
ShaderRecompiler::Decoder::DecodeProgram(std::span{shader}, decoded);
|
||||
graph = ShaderRecompiler::CFG::BuildGraph(decoded);
|
||||
typed = ShaderRecompiler::Frontend::TranslateProgram(decoded, graph,
|
||||
@@ -7348,7 +7348,6 @@ void TestNewShaderRecompilerStructuredU64Phi() {
|
||||
IR::IREmitter use(join);
|
||||
use.Emit(IR::ValueOpcode::CompositeExtractU64,
|
||||
{IR::Value(&phi), IR::Value(1u)});
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program);
|
||||
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(program, options.input_info);
|
||||
CheckSpirvBinaryValidates(spirv);
|
||||
@@ -8673,7 +8672,7 @@ void TestNewShaderRecompilerDispatcherSpillsU32x3() {
|
||||
{IR::Value(1u), IR::Value(2u), IR::Value(3u)});
|
||||
IR::IREmitter use(prologue_program.blocks[1]);
|
||||
use.Emit(IR::ValueOpcode::CompositeExtractU32x3, {vector, IR::Value(2u)});
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(prologue_program);
|
||||
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(prologue_program,
|
||||
options.input_info);
|
||||
CheckSpirvBinaryValidates(spirv);
|
||||
@@ -8692,7 +8691,6 @@ void TestNewShaderRecompilerDispatcherSpillsU32x3() {
|
||||
IR::IREmitter use(program.blocks[2]);
|
||||
use.Emit(IR::ValueOpcode::CompositeExtractU32x3, {vector, IR::Value(2u)});
|
||||
use.Emit(IR::ValueOpcode::CompositeExtractU32x3, {vector, IR::Value(1u)});
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program);
|
||||
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(program,
|
||||
options.input_info);
|
||||
@@ -8834,7 +8832,6 @@ void TestNewShaderRecompilerU64PairTranslation() {
|
||||
ir.Emit(IR::ValueOpcode::ShiftRightLogical64, {base, IR::Value(count)});
|
||||
ir.Emit(IR::ValueOpcode::ShiftRightArithmetic64, {base, IR::Value(count)});
|
||||
}
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program);
|
||||
|
||||
auto spirv = ShaderRecompiler::Spirv::EmitProgram(program, options.input_info);
|
||||
CheckSpirvBinaryValidates(spirv);
|
||||
@@ -8868,7 +8865,7 @@ void TestNewShaderRecompilerU64PairTranslation() {
|
||||
{IR::Value(1u), IR::Value(2u)});
|
||||
IR::IREmitter use(program.blocks[2]);
|
||||
use.Emit(IR::ValueOpcode::CompositeExtractU64, {vector, IR::Value(1u)});
|
||||
ShaderRecompiler::Spirv::AnalyzeProgramRequirements(program);
|
||||
|
||||
spirv.clear();
|
||||
spirv = ShaderRecompiler::Spirv::EmitProgram(program, options.input_info);
|
||||
CheckSpirvBinaryValidates(spirv);
|
||||
@@ -9429,7 +9426,7 @@ void TestEmbeddedVertexFormatSwizzle() {
|
||||
options.stage = ShaderType::Vertex;
|
||||
options.wave_size = 32;
|
||||
options.user_data_count = 0;
|
||||
options.vertex = &input;
|
||||
options.input_info.vertex = &input;
|
||||
options.embedded_fetch = &fetch;
|
||||
auto program = Frontend::TranslateProgram(decoded, CFG::BuildGraph(decoded), options);
|
||||
Check(program.info.vertex_fetch_components[0] == test.source_width,
|
||||
@@ -9528,7 +9525,7 @@ void TestMeshInputAssembly() {
|
||||
options.stage = ShaderType::Mesh;
|
||||
options.wave_size = 64;
|
||||
options.user_data_count = 0;
|
||||
options.vertex = &input;
|
||||
options.input_info.vertex = &input;
|
||||
auto program = Frontend::TranslateProgram(decoded, graph, options);
|
||||
const uint32_t draw[] = {test.count, test.base_vertex, 7, test.width,
|
||||
test.address_low, 0x12};
|
||||
@@ -9660,7 +9657,7 @@ void TestNewShaderRecompilerPrunesUnreachableSetpcMetadata() {
|
||||
ShaderRecompiler::Frontend::TranslateOptions translate_options{};
|
||||
translate_options.stage = ShaderType::Compute;
|
||||
translate_options.wave_size = 64u;
|
||||
translate_options.compute = &compute;
|
||||
translate_options.input_info.compute = &compute;
|
||||
ir = ShaderRecompiler::Frontend::TranslateProgram(decoded, graph,
|
||||
translate_options);
|
||||
size_t scalar_loads = 0;
|
||||
@@ -9946,43 +9943,104 @@ void TestNewShaderRecompilerAuxPositionExports() {
|
||||
"PA_CL_VS_OUT_CNTL is absent from the vertex shader cache key");
|
||||
}
|
||||
|
||||
void TestDeferredSpirvPhiPatching() {
|
||||
ShaderRecompiler::Spirv::Builder builder;
|
||||
const auto type = builder.Type(spv::OpTypeInt, {32u, 0u});
|
||||
const auto zero = builder.Constant(spv::OpConstant, type, {0u});
|
||||
const auto one = builder.Constant(spv::OpConstant, type, {1u});
|
||||
const auto left = builder.AllocateId();
|
||||
const auto right = builder.AllocateId();
|
||||
const auto result = builder.AllocateId();
|
||||
const auto phi = builder.AddDeferredPhi(type, result, 2);
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
ExpectFatal([&] { (void)builder.Build(); }, "unpatched phi was serialized");
|
||||
ExpectFatal([&] { builder.PatchDeferredPhi(phi, 2, one, right); },
|
||||
"phi patch accepted an incoming outside its encoded length");
|
||||
#endif
|
||||
|
||||
builder.PatchDeferredPhi(phi, 1, one, right);
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
ExpectFatal([&] { builder.PatchDeferredPhi(phi, 1, zero, left); },
|
||||
"phi incoming was patched twice");
|
||||
ExpectFatal([&] { (void)builder.Build(); },
|
||||
"partially patched phi was serialized");
|
||||
#endif
|
||||
|
||||
// Appending another instruction must not change the first phi's patch
|
||||
// location.
|
||||
const auto second_result = builder.AllocateId();
|
||||
const auto second_phi = builder.AddDeferredPhi(type, second_result, 1);
|
||||
builder.PatchDeferredPhi(phi, 0, zero, left);
|
||||
builder.PatchDeferredPhi(second_phi, 0, one, right);
|
||||
const auto binary = builder.Build();
|
||||
uint32_t phis = 0;
|
||||
for (size_t offset = 5; offset < binary.size();) {
|
||||
const auto words = binary[offset] >> spv::WordCountShift;
|
||||
if ((binary[offset] & spv::OpCodeMask) == spv::OpPhi) {
|
||||
if (phis++ == 0) {
|
||||
Check(words == 7 && binary[offset + 2] == result &&
|
||||
binary[offset + 3] == zero && binary[offset + 4] == left &&
|
||||
binary[offset + 5] == one && binary[offset + 6] == right,
|
||||
"out-of-order phi patches changed incoming values or parents");
|
||||
} else {
|
||||
Check(words == 5 && binary[offset + 2] == second_result &&
|
||||
binary[offset + 3] == one && binary[offset + 4] == right,
|
||||
"patching one phi corrupted its neighbor");
|
||||
}
|
||||
}
|
||||
offset += words;
|
||||
}
|
||||
Check(phis == 2, "deferred phi instructions were lost");
|
||||
}
|
||||
|
||||
void TestDemandDrivenSpirvDeclarations() {
|
||||
using ShaderRecompiler::Spirv::Builder;
|
||||
|
||||
Builder declarations;
|
||||
const auto uint_type = declarations.Type(21u, {32u, 0u});
|
||||
Check(uint_type == declarations.Type(21u, {32u, 0u}),
|
||||
const auto uint_type = declarations.Type(spv::OpTypeInt, {32u, 0u});
|
||||
Check(uint_type == declarations.Type(spv::OpTypeInt, {32u, 0u}),
|
||||
"structural type interning returned different IDs");
|
||||
const auto zero = declarations.Constant(43u, uint_type, {0u});
|
||||
Check(zero == declarations.Constant(43u, uint_type, {0u}),
|
||||
const auto zero = declarations.Constant(spv::OpConstant, uint_type, {0u});
|
||||
Check(zero == declarations.Constant(spv::OpConstant, uint_type, {0u}),
|
||||
"constant interning returned different IDs");
|
||||
Check(declarations.Import("GLSL.std.450") ==
|
||||
declarations.Import("GLSL.std.450"),
|
||||
"extended-instruction import was duplicated");
|
||||
declarations.RequireCapability(1u);
|
||||
declarations.RequireCapability(1u);
|
||||
const auto plain_array = declarations.Type(28u, {uint_type, zero});
|
||||
declarations.RequireCapability(spv::CapabilityShader);
|
||||
declarations.RequireCapability(spv::CapabilityShader);
|
||||
const auto plain_array =
|
||||
declarations.Type(spv::OpTypeArray, {uint_type, zero});
|
||||
const auto decorated_array = declarations.DecoratedType(
|
||||
28u, {uint_type, zero}, {{71u, {6u, sizeof(uint32_t)}}});
|
||||
spv::OpTypeArray, {uint_type, zero},
|
||||
{{spv::OpDecorate, {spv::DecorationArrayStride, sizeof(uint32_t)}}});
|
||||
Check(decorated_array ==
|
||||
declarations.DecoratedType(28u, {uint_type, zero},
|
||||
{{71u, {6u, sizeof(uint32_t)}}}),
|
||||
declarations.DecoratedType(
|
||||
spv::OpTypeArray, {uint_type, zero},
|
||||
{{spv::OpDecorate,
|
||||
{spv::DecorationArrayStride, sizeof(uint32_t)}}}),
|
||||
"decorated type interning returned different IDs");
|
||||
Check(plain_array != decorated_array,
|
||||
"decorated and undecorated aggregate types were aliased");
|
||||
Check(declarations.DecoratedType(28u, {uint_type, zero}, {}) == plain_array,
|
||||
Check(declarations.DecoratedType(spv::OpTypeArray, {uint_type, zero}, {}) ==
|
||||
plain_array,
|
||||
"empty decorated type request bypassed structural interning");
|
||||
const auto ptr = declarations.Type(32u, {7u, uint_type});
|
||||
Check(declarations.DefineGlobalVariable(ptr, 7u) !=
|
||||
declarations.DefineGlobalVariable(ptr, 7u),
|
||||
const auto ptr = declarations.Type(spv::OpTypePointer,
|
||||
{spv::StorageClassFunction, uint_type});
|
||||
Check(declarations.DefineGlobalVariable(ptr, spv::StorageClassFunction) !=
|
||||
declarations.DefineGlobalVariable(ptr, spv::StorageClassFunction),
|
||||
"distinct variables were structurally interned");
|
||||
const auto declaration_binary = declarations.Build();
|
||||
Check(SpirvInstructionOpcodeCount(declaration_binary, 17u) == 1u &&
|
||||
SpirvInstructionOpcodeCount(declaration_binary, 11u) == 1u &&
|
||||
SpirvInstructionOpcodeCount(declaration_binary, 21u) == 1u &&
|
||||
SpirvInstructionOpcodeCount(declaration_binary, 28u) == 2u &&
|
||||
SpirvInstructionOpcodeCount(declaration_binary, 43u) == 1u &&
|
||||
SpirvInstructionOpcodeCount(declaration_binary, 71u) == 1u &&
|
||||
SpirvInstructionOpcodeCount(declaration_binary, spv::OpTypeInt) ==
|
||||
1u &&
|
||||
SpirvInstructionOpcodeCount(declaration_binary, spv::OpTypeArray) ==
|
||||
2u &&
|
||||
SpirvInstructionOpcodeCount(declaration_binary, spv::OpConstant) ==
|
||||
1u &&
|
||||
SpirvInstructionOpcodeCount(declaration_binary, spv::OpDecorate) ==
|
||||
1u &&
|
||||
SpirvInstructionOpcodeCount(declaration_binary, 59u) == 2u,
|
||||
"canonical builder emitted duplicate declarations");
|
||||
|
||||
@@ -10074,7 +10132,7 @@ void TestTypedEntryStateIsMinimal() {
|
||||
translate_options.stage = ShaderType::Compute;
|
||||
translate_options.wave_size = wave_size;
|
||||
translate_options.user_data_count = 0;
|
||||
translate_options.compute = &compute;
|
||||
translate_options.input_info.compute = &compute;
|
||||
values = ShaderRecompiler::Frontend::TranslateProgram(decoded, graph,
|
||||
translate_options);
|
||||
|
||||
@@ -11416,7 +11474,7 @@ void BuildTypedPlan(const uint32_t *code, uint32_t words,
|
||||
ShaderRecompiler::Frontend::TranslateOptions options{};
|
||||
options.stage = ShaderType::Compute;
|
||||
options.wave_size = 64u;
|
||||
options.compute = &compute;
|
||||
options.input_info.compute = &compute;
|
||||
ir = ShaderRecompiler::Frontend::TranslateProgram(decoded, cfg, options);
|
||||
ShaderRecompiler::IR::RewriteToSsa(ir.blocks);
|
||||
ShaderRecompiler::IR::ConstantPropagationPass(ir.blocks);
|
||||
@@ -12256,7 +12314,7 @@ void TestComputeLdsAllocationIdentity() {
|
||||
options.user_data = params.user_data;
|
||||
options.input_info.compute = &input_info;
|
||||
options.wave_size = input_info.wave_size;
|
||||
options.scratch_dwords = input_info.scratch_size_dwords;
|
||||
|
||||
auto result = RecompileForTest(shader, options);
|
||||
Check(MeasureSpirv(result.spirv).workgroup_variables == 1u,
|
||||
"LDS shader did not declare exactly one Workgroup variable");
|
||||
@@ -12322,7 +12380,7 @@ void TestComputeLdsAllocationIdentity() {
|
||||
scratch_options.user_data = scratch_params.user_data;
|
||||
scratch_options.input_info.compute = &scratch_info;
|
||||
scratch_options.wave_size = scratch_info.wave_size;
|
||||
scratch_options.scratch_dwords = scratch_info.scratch_size_dwords;
|
||||
|
||||
auto scratch_result =
|
||||
RecompileForTest(scratch_params.code, scratch_options);
|
||||
Check(scratch_result.program.scratch_dwords == 7,
|
||||
@@ -12482,7 +12540,10 @@ void TestNewShaderRecompilerFlatAddressDomainsUseDma() {
|
||||
|
||||
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
options.user_data = user_data;
|
||||
options.scratch_dwords = 1;
|
||||
ShaderComputeInputInfo compute{};
|
||||
compute.scratch_size_dwords = 1;
|
||||
options.input_info.compute = &compute;
|
||||
|
||||
auto result = RecompileForTest(segmented_shader, options);
|
||||
Check(result.program.info.uses_dma,
|
||||
"GLOBAL null-SADDR did not enable DMA");
|
||||
@@ -12491,6 +12552,125 @@ void TestNewShaderRecompilerFlatAddressDomainsUseDma() {
|
||||
"SCRATCH incorrectly entered guest address tracking");
|
||||
}
|
||||
|
||||
void TestCompilerStageInputOwnership() {
|
||||
using namespace ShaderRecompiler;
|
||||
const uint32_t shader[] = {EncodeSopp(0x01)};
|
||||
ShaderVertexInputInfo vertex{};
|
||||
vertex.scratch_size_dwords = 3;
|
||||
vertex.mesh.scratch_size_dwords = 11;
|
||||
vertex.mesh.threads_num[0] = 192;
|
||||
vertex.mesh.threads_num[1] = vertex.mesh.threads_num[2] = 1;
|
||||
vertex.mesh.primitives_per_group = 62;
|
||||
vertex.mesh.vertices_per_group = 64;
|
||||
ShaderPixelInputInfo pixel{};
|
||||
pixel.scratch_size_dwords = 5;
|
||||
ShaderComputeInputInfo compute{};
|
||||
compute.scratch_size_dwords = 7;
|
||||
|
||||
for (const auto stage : {ShaderType::Vertex, ShaderType::Pixel,
|
||||
ShaderType::Compute, ShaderType::Mesh}) {
|
||||
CompileOptions options{};
|
||||
options.stage = stage;
|
||||
options.dump_ir = false;
|
||||
uint32_t expected_scratch = 0;
|
||||
switch (stage) {
|
||||
case ShaderType::Vertex:
|
||||
options.input_info.vertex = &vertex;
|
||||
expected_scratch = 3;
|
||||
break;
|
||||
case ShaderType::Pixel:
|
||||
options.input_info.pixel = &pixel;
|
||||
expected_scratch = 5;
|
||||
break;
|
||||
case ShaderType::Compute:
|
||||
options.input_info.compute = &compute;
|
||||
expected_scratch = 7;
|
||||
break;
|
||||
case ShaderType::Mesh:
|
||||
options.input_info.vertex = &vertex;
|
||||
expected_scratch = 11;
|
||||
break;
|
||||
default:
|
||||
std::abort();
|
||||
}
|
||||
const auto translated = TranslateProgram(shader, options);
|
||||
Check(
|
||||
translated.program.scratch_dwords == expected_scratch,
|
||||
"translation did not use its stage's authoritative scratch allocation");
|
||||
}
|
||||
|
||||
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
options.shader_hash = 0x1234;
|
||||
auto translated = TranslateProgram(shader, options);
|
||||
options.stage = ShaderType::Unknown;
|
||||
options.shader_hash = 0;
|
||||
auto compiled = CompileProgram(std::move(translated), options, {});
|
||||
Check(compiled.program.stage == ShaderType::Compute &&
|
||||
compiled.program.shader_hash == 0x1234,
|
||||
"compilation reinterpreted the translated program's identity");
|
||||
CheckSpirvBinaryValidates(compiled.spirv);
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
|
||||
options.stage = ShaderType::Vertex;
|
||||
options.input_info.vertex = nullptr;
|
||||
ExpectFatal([&] { (void)TranslateProgram(shader, options); },
|
||||
"missing stage metadata bypassed translation validation");
|
||||
const uint32_t auxiliary_position[] = {
|
||||
EncodeExp0(0x0d, 0x1), EncodeExp1(0, 0, 0, 0), EncodeSopp(0x01)};
|
||||
for (const auto stage : {ShaderType::Pixel, ShaderType::Compute}) {
|
||||
ExpectFatal(
|
||||
[&] {
|
||||
(void)RecompileForTest(auxiliary_position, MakeCompileOptions(stage));
|
||||
},
|
||||
"auxiliary position export read another stage's input metadata");
|
||||
}
|
||||
Decoder::Program decoded;
|
||||
Decoder::DecodeProgram(shader, decoded);
|
||||
const auto cfg = CFG::BuildGraph(decoded);
|
||||
Frontend::EmbeddedFetchPlan fetch;
|
||||
Frontend::TranslateOptions invalid_fetch{.stage = ShaderType::Compute,
|
||||
.input_info = {.compute = &compute},
|
||||
.embedded_fetch = &fetch};
|
||||
ExpectFatal(
|
||||
[&] { (void)Frontend::TranslateProgram(decoded, cfg, invalid_fetch); },
|
||||
"embedded vertex fetch accepted another stage's metadata");
|
||||
#endif
|
||||
}
|
||||
|
||||
void TestSpirvEmissionOwnsRequirements() {
|
||||
using namespace ShaderRecompiler;
|
||||
const uint32_t shader[] = {EncodeSopp(0x01)};
|
||||
const auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
auto compiled = RecompileForTest(shader, options);
|
||||
IR::IREmitter emitter(compiled.program.blocks.front());
|
||||
emitter.Emit(IR::ValueOpcode::LaneId, {});
|
||||
const auto binary = Spirv::EmitProgram(compiled.program, options.input_info);
|
||||
CheckSpirvBinaryValidates(binary);
|
||||
Check(Common::ContainsStr(DisassembleSpirvBinary(binary),
|
||||
"BuiltIn SubgroupLocalInvocationId"),
|
||||
"emission reused requirements from an earlier IR version");
|
||||
}
|
||||
|
||||
void TestRepeatedExportsHaveOneInterface() {
|
||||
const uint32_t shader[] = {EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3),
|
||||
EncodeExp0(0x0c, 0xf), EncodeExp1(0, 1, 2, 3),
|
||||
EncodeSopp(0x01)};
|
||||
const auto result =
|
||||
RecompileForTest(shader, MakeCompileOptions(ShaderType::Vertex));
|
||||
Check(result.program.info.outputs.size() == 1,
|
||||
"repeated exports duplicated the collected shader interface");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
uint32_t outputs = 0;
|
||||
for (size_t offset = 5; offset < result.spirv.size();) {
|
||||
if ((result.spirv[offset] & spv::OpCodeMask) == spv::OpVariable &&
|
||||
result.spirv[offset + 3] == spv::StorageClassOutput) {
|
||||
outputs++;
|
||||
}
|
||||
offset += result.spirv[offset] >> spv::WordCountShift;
|
||||
}
|
||||
Check(outputs == 1, "repeated exports produced duplicate output variables");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerSpirvSizeBaselines() {
|
||||
const auto compile = [](const char *name, std::span<const uint32_t> shader,
|
||||
const SpirvMetrics &budget,
|
||||
@@ -12758,6 +12938,10 @@ int main() {
|
||||
TestNativeShaderResourceDependencies();
|
||||
TestNormalizedImageContracts();
|
||||
TestSpirvRequirementsAnalysis();
|
||||
TestDeferredSpirvPhiPatching();
|
||||
TestCompilerStageInputOwnership();
|
||||
TestSpirvEmissionOwnsRequirements();
|
||||
TestRepeatedExportsHaveOneInterface();
|
||||
TestNewShaderRecompilerSpirvSizeBaselines();
|
||||
TestDemandDrivenSpirvDeclarations();
|
||||
TestNewShaderRecompilerSMovB32();
|
||||
|
||||
Reference in New Issue
Block a user