mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
refactor(shader): remove register-state compatibility paths
This commit is contained in:
@@ -232,51 +232,6 @@ static void ReportReserveExceeded(const IR::Program& program, const char* vector
|
||||
InitialEmitterVectorReserve);
|
||||
}
|
||||
|
||||
void CollectValueRegisterMirrors(const IR::ValueProgram& program,
|
||||
std::vector<Emitter::RegisterBinding>& registers) {
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::ValueOpcode::SetScalarRegister:
|
||||
case IR::ValueOpcode::SetThreadBitScalarRegister:
|
||||
if (block != program.blocks.front()) {
|
||||
Emitter::CollectRegister(
|
||||
registers,
|
||||
{IR::RegisterFile::Scalar, IR::RegIndex(inst.Arg(0).ScalarRegister())});
|
||||
}
|
||||
break;
|
||||
case IR::ValueOpcode::SetVectorRegister:
|
||||
Emitter::CollectRegister(
|
||||
registers,
|
||||
{IR::RegisterFile::Vector, IR::RegIndex(inst.Arg(0).VectorRegister())});
|
||||
break;
|
||||
case IR::ValueOpcode::SetM0:
|
||||
if (block != program.blocks.front()) {
|
||||
Emitter::CollectRegister(registers, {IR::RegisterFile::M0, 0});
|
||||
}
|
||||
break;
|
||||
case IR::ValueOpcode::SetExecLo:
|
||||
case IR::ValueOpcode::SetExecHi:
|
||||
if (block != program.blocks.front()) {
|
||||
Emitter::CollectRegister(
|
||||
registers, {IR::RegisterFile::Exec,
|
||||
inst.GetOpcode() == IR::ValueOpcode::SetExecLo ? 0u : 1u});
|
||||
}
|
||||
break;
|
||||
case IR::ValueOpcode::SetVccLo:
|
||||
case IR::ValueOpcode::SetVccHi:
|
||||
if (block != program.blocks.front()) {
|
||||
Emitter::CollectRegister(
|
||||
registers, {IR::RegisterFile::Vcc,
|
||||
inst.GetOpcode() == IR::ValueOpcode::SetVccLo ? 0u : 1u});
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IR::SpirvRequirements GetProgramRequirements(const IR::Program& program) {
|
||||
if (program.spirv_requirements.has_value()) {
|
||||
return *program.spirv_requirements;
|
||||
@@ -418,14 +373,9 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
|
||||
state.stage = program.stage;
|
||||
state.wave_size = program.wave_size;
|
||||
state.per_invocation_masks = program.lane_mask_mode == ShaderLaneMaskMode::PerInvocation;
|
||||
state.exact_subgroup_operations =
|
||||
!state.per_invocation_masks && requirements.requires_exact_subgroup;
|
||||
state.registers.reserve(InitialEmitterVectorReserve);
|
||||
state.inputs.reserve(InitialEmitterVectorReserve);
|
||||
state.outputs.reserve(InitialEmitterVectorReserve);
|
||||
state.interface_variables.reserve(InitialEmitterVectorReserve);
|
||||
state.reachable_blocks.reserve(InitialEmitterVectorReserve);
|
||||
CollectValueRegisterMirrors(value_program, state.registers);
|
||||
CopyProgramInputsAndOutputs(state, program);
|
||||
state.needs_subgroup_ballot = requirements.subgroup_ballot;
|
||||
state.needs_subgroup_shuffle = requirements.subgroup_shuffle;
|
||||
@@ -438,16 +388,13 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
|
||||
AllocateOutputVariables(state);
|
||||
AllocateDescriptorVariables(state);
|
||||
EmitHeaderAndTypes(state);
|
||||
AllocateRegisterVariables(state);
|
||||
if (!EmitValueProgram(state, value_program, error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReportReserveExceeded(program, "registers", state.registers.size());
|
||||
ReportReserveExceeded(program, "inputs", state.inputs.size());
|
||||
ReportReserveExceeded(program, "outputs", state.outputs.size());
|
||||
ReportReserveExceeded(program, "interface_variables", state.interface_variables.size());
|
||||
ReportReserveExceeded(program, "reachable_blocks", state.reachable_blocks.size());
|
||||
|
||||
auto binary = state.builder.Build();
|
||||
if (binary.empty()) {
|
||||
|
||||
@@ -38,18 +38,6 @@ void SetError(std::string* error, const char* message) {
|
||||
}
|
||||
}
|
||||
|
||||
void CollectRegister(std::vector<RegisterBinding>& registers, IR::Register reg) {
|
||||
if (std::any_of(registers.begin(), registers.end(),
|
||||
[reg](const RegisterBinding& binding) { return binding.reg == reg; })) {
|
||||
return;
|
||||
}
|
||||
registers.push_back({reg, 0});
|
||||
}
|
||||
|
||||
bool IsInactiveWave32ExecHigh(const EmitterState& state, IR::Register reg) {
|
||||
return state.wave_size == 32u && reg.file == IR::RegisterFile::Exec && reg.index == 1u;
|
||||
}
|
||||
|
||||
bool HasOutput(const std::vector<OutputBinding>& outputs, IR::StageOutputKind kind,
|
||||
uint32_t index) {
|
||||
return std::any_of(outputs.begin(), outputs.end(), [kind, index](const OutputBinding& binding) {
|
||||
@@ -88,15 +76,6 @@ uint32_t OutputVariableForExport(const EmitterState& state, const IR::ExportInfo
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t PointerForRegister(const EmitterState& state, IR::Register reg) {
|
||||
for (const auto& binding: state.registers) {
|
||||
if (binding.reg == reg) {
|
||||
return binding.pointer_id;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t ConstantU32(EmitterState& state, uint32_t value);
|
||||
[[noreturn]] void ExitDescriptorBindingFailure(const EmitterState& state,
|
||||
IR::DescriptorBindingKind kind, uint32_t resource,
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
#include "graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
|
||||
bool UserDataDwordIndex(const EmitterState& state, IR::Register reg, uint32_t& dword_index) {
|
||||
if (reg.file != IR::RegisterFile::Scalar) {
|
||||
return false;
|
||||
}
|
||||
const auto& registers = state.program.bindings.user_data_registers;
|
||||
const auto found = std::lower_bound(registers.begin(), registers.end(), reg.index);
|
||||
if (found == registers.end() || *found != reg.index) {
|
||||
return false;
|
||||
}
|
||||
dword_index = static_cast<uint32_t>(found - registers.begin());
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t EmitShaderDataDwordLoad(EmitterState& state, uint32_t dword_index) {
|
||||
if (state.push_constant_variable != 0) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, state.ptr_push_constant_uint, pointer,
|
||||
state.push_constant_variable, ConstantU32(state, 0),
|
||||
ConstantU32(state, dword_index)});
|
||||
state.builder.AddFunction({OpLoad, state.uint_type, value, pointer});
|
||||
return value;
|
||||
}
|
||||
if (state.vsharp_storage_variable != 0) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer_uint, pointer,
|
||||
state.vsharp_storage_variable, ConstantU32(state, 0),
|
||||
ConstantU32(state, dword_index)});
|
||||
state.builder.AddFunction({OpLoad, state.uint_type, value, pointer});
|
||||
return value;
|
||||
}
|
||||
return ConstantU32(state, 0);
|
||||
}
|
||||
|
||||
uint32_t InitialRegisterValue(const EmitterState& state, IR::Register reg) {
|
||||
if (reg.file != IR::RegisterFile::Exec) {
|
||||
return 0;
|
||||
}
|
||||
if (state.per_invocation_masks) {
|
||||
return reg.index == 0 ? 1u : 0u;
|
||||
}
|
||||
return reg.index == 0 || state.wave_size == 64u ? 0xffffffffu : 0u;
|
||||
}
|
||||
|
||||
} // namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter
|
||||
@@ -5,6 +5,19 @@
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
namespace {
|
||||
|
||||
bool UserDataDwordIndex(const EmitterState& state, IR::Register reg, uint32_t& dword_index) {
|
||||
if (reg.file != IR::RegisterFile::Scalar) {
|
||||
return false;
|
||||
}
|
||||
const auto& registers = state.program.bindings.user_data_registers;
|
||||
const auto found = std::lower_bound(registers.begin(), registers.end(), reg.index);
|
||||
if (found == registers.end() || *found != reg.index) {
|
||||
return false;
|
||||
}
|
||||
dword_index = static_cast<uint32_t>(found - registers.begin());
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t EmitWqmLaneU32(EmitterState& state, uint32_t src) {
|
||||
uint32_t ret = ConstantU32(state, 0);
|
||||
for (uint32_t i = 0; i < 8u; i++) {
|
||||
@@ -32,19 +45,6 @@ uint32_t BoolAsU32(ValueEmitContext& ctx, uint32_t value) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void StoreRegisterMirror(ValueEmitContext& ctx, IR::Register reg, uint32_t value) {
|
||||
if (const auto pointer = PointerForRegister(ctx.state, reg); pointer != 0) {
|
||||
if (IsInactiveWave32ExecHigh(ctx.state, reg)) {
|
||||
value = ConstantU32(ctx.state, 0);
|
||||
}
|
||||
ctx.state.builder.AddFunction({OpStore, pointer, value});
|
||||
}
|
||||
}
|
||||
|
||||
bool IsPrologue(const ValueEmitContext& ctx) {
|
||||
return !ctx.program.blocks.empty() && ctx.current_block == ctx.program.blocks.front();
|
||||
}
|
||||
|
||||
uint32_t EmitBuiltinU32(ValueEmitContext& ctx, IR::StageInputKind kind, uint32_t component) {
|
||||
auto& state = ctx.state;
|
||||
if (kind == IR::StageInputKind::LocalInvocationIndex) {
|
||||
@@ -338,61 +338,9 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::ValueOpcode::Identity:
|
||||
case IR::ValueOpcode::ConditionRef: ctx.Define(inst, ctx.Arg(inst, 0)); return true;
|
||||
case IR::ValueOpcode::SetScalarRegister:
|
||||
StoreRegisterMirror(
|
||||
ctx, {IR::RegisterFile::Scalar, IR::RegIndex(inst.Arg(0).ScalarRegister())},
|
||||
ctx.Arg(inst, 1));
|
||||
return true;
|
||||
case IR::ValueOpcode::SetThreadBitScalarRegister:
|
||||
StoreRegisterMirror(
|
||||
ctx, {IR::RegisterFile::Scalar, IR::RegIndex(inst.Arg(0).ScalarRegister())},
|
||||
BoolAsU32(ctx, ctx.Arg(inst, 1)));
|
||||
return true;
|
||||
case IR::ValueOpcode::SetVectorRegister:
|
||||
StoreRegisterMirror(
|
||||
ctx, {IR::RegisterFile::Vector, IR::RegIndex(inst.Arg(0).VectorRegister())},
|
||||
ctx.Arg(inst, 1));
|
||||
return true;
|
||||
case IR::ValueOpcode::SetScc: ctx.Arg(inst, 0); return true;
|
||||
case IR::ValueOpcode::SetExec: ctx.Arg(inst, 0); return true;
|
||||
case IR::ValueOpcode::SetExecLo:
|
||||
case IR::ValueOpcode::SetExecHi:
|
||||
if (!IsPrologue(ctx)) {
|
||||
StoreRegisterMirror(ctx,
|
||||
{IR::RegisterFile::Exec,
|
||||
inst.GetOpcode() == IR::ValueOpcode::SetExecLo ? 0u : 1u},
|
||||
ctx.Arg(inst, 0));
|
||||
}
|
||||
return true;
|
||||
case IR::ValueOpcode::SetVcc: ctx.Arg(inst, 0); return true;
|
||||
case IR::ValueOpcode::SetVccLo:
|
||||
case IR::ValueOpcode::SetVccHi:
|
||||
if (!IsPrologue(ctx)) {
|
||||
StoreRegisterMirror(ctx,
|
||||
{IR::RegisterFile::Vcc,
|
||||
inst.GetOpcode() == IR::ValueOpcode::SetVccLo ? 0u : 1u},
|
||||
ctx.Arg(inst, 0));
|
||||
}
|
||||
return true;
|
||||
case IR::ValueOpcode::SetM0:
|
||||
StoreRegisterMirror(ctx, {IR::RegisterFile::M0, 0}, ctx.Arg(inst, 0));
|
||||
return true;
|
||||
case IR::ValueOpcode::GetScalarRegister:
|
||||
case IR::ValueOpcode::GetThreadBitScalarRegister:
|
||||
case IR::ValueOpcode::GetVectorRegister:
|
||||
case IR::ValueOpcode::GetScc:
|
||||
case IR::ValueOpcode::GetExec:
|
||||
case IR::ValueOpcode::GetExecLo:
|
||||
case IR::ValueOpcode::GetExecHi:
|
||||
case IR::ValueOpcode::GetVcc:
|
||||
case IR::ValueOpcode::GetVccLo:
|
||||
case IR::ValueOpcode::GetVccHi:
|
||||
case IR::ValueOpcode::GetM0: return true;
|
||||
case IR::ValueOpcode::Void:
|
||||
case IR::ValueOpcode::Reference:
|
||||
case IR::ValueOpcode::ReferenceU32:
|
||||
case IR::ValueOpcode::Prologue:
|
||||
case IR::ValueOpcode::Epilogue:
|
||||
case IR::ValueOpcode::ControlNop:
|
||||
case IR::ValueOpcode::Waitcnt:
|
||||
case IR::ValueOpcode::Sendmsg:
|
||||
|
||||
@@ -273,11 +273,6 @@ enum : uint32_t {
|
||||
GlslFindUMsb = 75,
|
||||
};
|
||||
|
||||
struct RegisterBinding {
|
||||
IR::Register reg;
|
||||
uint32_t pointer_id = 0;
|
||||
};
|
||||
|
||||
struct InputBinding {
|
||||
IR::StageInputKind kind = IR::StageInputKind::VertexIndex;
|
||||
uint32_t location = 0;
|
||||
@@ -327,7 +322,6 @@ struct EmitterState {
|
||||
ShaderStageInputInfo input_info;
|
||||
ShaderType stage = ShaderType::Unknown;
|
||||
uint32_t wave_size = 64;
|
||||
bool exact_subgroup_operations = false;
|
||||
bool per_invocation_masks = false;
|
||||
uint32_t void_type = 0;
|
||||
uint32_t bool_type = 0;
|
||||
@@ -426,15 +420,9 @@ struct EmitterState {
|
||||
bool needs_image_gather_extended = false;
|
||||
bool needs_function_lds = false;
|
||||
bool needs_pixel_valid_mask = false;
|
||||
bool unsupported_ir_instruction = false;
|
||||
IR::Opcode unsupported_ir_opcode = IR::Opcode::Count;
|
||||
uint32_t unsupported_ir_pc = 0;
|
||||
std::vector<RegisterBinding> registers;
|
||||
std::vector<InputBinding> inputs;
|
||||
std::vector<OutputBinding> outputs;
|
||||
std::vector<uint32_t> interface_variables;
|
||||
std::vector<bool> reachable_blocks;
|
||||
std::map<uint32_t, uint32_t> block_labels;
|
||||
std::map<uint32_t, uint32_t> constants;
|
||||
std::map<uint32_t, uint32_t> signed_constants;
|
||||
std::map<uint32_t, uint32_t> float_constants;
|
||||
@@ -621,18 +609,12 @@ uint32_t VertexParameterInputPointerType(const EmitterState& state, VertexInputS
|
||||
|
||||
void SetError(std::string* error, const char* message);
|
||||
|
||||
void CollectRegister(std::vector<RegisterBinding>& registers, IR::Register reg);
|
||||
|
||||
bool IsInactiveWave32ExecHigh(const EmitterState& state, IR::Register reg);
|
||||
|
||||
bool HasOutput(const std::vector<OutputBinding>& outputs, IR::StageOutputKind kind, uint32_t index);
|
||||
|
||||
void CopyProgramInputsAndOutputs(EmitterState& state, const IR::Program& program);
|
||||
|
||||
uint32_t OutputVariableForExport(const EmitterState& state, const IR::ExportInfo& exp);
|
||||
|
||||
uint32_t PointerForRegister(const EmitterState& state, IR::Register reg);
|
||||
|
||||
uint32_t ConstantU32(EmitterState& state, uint32_t value);
|
||||
|
||||
uint32_t EmitSubgroupLocalInvocationId(EmitterState& state);
|
||||
@@ -714,8 +696,6 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state);
|
||||
|
||||
void EmitHeaderAndTypes(EmitterState& state);
|
||||
|
||||
void AllocateRegisterVariables(EmitterState& state);
|
||||
|
||||
void AllocateDescriptorVariables(EmitterState& state);
|
||||
|
||||
uint32_t EmitTrueBool(EmitterState& state);
|
||||
@@ -754,8 +734,6 @@ uint32_t EmitBinaryU32(EmitterState& state, uint32_t opcode, uint32_t lhs, uint3
|
||||
|
||||
uint32_t EmitShaderDataDwordLoad(EmitterState& state, uint32_t dword_index);
|
||||
|
||||
bool UserDataDwordIndex(const EmitterState& state, IR::Register reg, uint32_t& dword_index);
|
||||
|
||||
uint32_t StorageBufferPackedStride(const EmitterState& state, const IR::MemoryInfo& mem,
|
||||
uint32_t use_pc);
|
||||
|
||||
@@ -878,8 +856,6 @@ uint32_t EmitFAbsValue(EmitterState& state, uint32_t value);
|
||||
|
||||
uint32_t EmitF16BitsToF32(EmitterState& state, uint32_t bits);
|
||||
|
||||
uint32_t InitialRegisterValue(const EmitterState& state, IR::Register reg);
|
||||
|
||||
bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
|
||||
bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||
|
||||
@@ -2,6 +2,28 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||
|
||||
uint32_t EmitShaderDataDwordLoad(EmitterState& state, uint32_t dword_index) {
|
||||
if (state.push_constant_variable != 0) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, state.ptr_push_constant_uint, pointer,
|
||||
state.push_constant_variable, ConstantU32(state, 0),
|
||||
ConstantU32(state, dword_index)});
|
||||
state.builder.AddFunction({OpLoad, state.uint_type, value, pointer});
|
||||
return value;
|
||||
}
|
||||
if (state.vsharp_storage_variable != 0) {
|
||||
const auto pointer = state.builder.AllocateId();
|
||||
const auto value = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpAccessChain, state.ptr_storage_buffer_uint, pointer,
|
||||
state.vsharp_storage_variable, ConstantU32(state, 0),
|
||||
ConstantU32(state, dword_index)});
|
||||
state.builder.AddFunction({OpLoad, state.uint_type, value, pointer});
|
||||
return value;
|
||||
}
|
||||
return ConstantU32(state, 0);
|
||||
}
|
||||
|
||||
uint32_t EmitAddU32(EmitterState& state, uint32_t lhs, uint32_t rhs) {
|
||||
const auto ret = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpIAdd, state.uint_type, ret, lhs, rhs});
|
||||
|
||||
@@ -803,18 +803,6 @@ void EmitHeaderAndTypes(EmitterState& state) {
|
||||
state.builder.AddType({OpTypeFunction, state.func_type, state.void_type});
|
||||
}
|
||||
|
||||
void AllocateRegisterVariables(EmitterState& state) {
|
||||
if (state.stage == ShaderType::Pixel && state.needs_pixel_valid_mask) {
|
||||
state.pixel_valid_mask_variable = state.builder.AllocateId();
|
||||
state.builder.AddName(state.pixel_valid_mask_variable, "pixel_valid_mask_active");
|
||||
}
|
||||
for (auto& binding: state.registers) {
|
||||
binding.pointer_id = state.builder.AllocateId();
|
||||
const auto name = IR::RegisterToString(binding.reg);
|
||||
state.builder.AddName(binding.pointer_id, name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void AllocateDescriptorVariables(EmitterState& state) {
|
||||
if (DescriptorBinding(state, IR::DescriptorBindingKind::Buffers) != nullptr) {
|
||||
state.storage_buffer_variable = state.builder.AllocateId();
|
||||
|
||||
@@ -430,6 +430,10 @@ void ValueEmitContext::Fail(const IR::Inst& inst, const char* reason) {
|
||||
|
||||
bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std::string* error) {
|
||||
ValueEmitContext ctx(state, program);
|
||||
if (state.stage == ShaderType::Pixel && state.needs_pixel_valid_mask) {
|
||||
state.pixel_valid_mask_variable = state.builder.AllocateId();
|
||||
state.builder.AddName(state.pixel_valid_mask_variable, "pixel_valid_mask_active");
|
||||
}
|
||||
for (const auto* block: program.blocks) {
|
||||
ctx.labels.emplace(block, state.builder.AllocateId());
|
||||
for (const auto& inst: *block) {
|
||||
@@ -512,10 +516,6 @@ bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std:
|
||||
state.builder.AddFunction(
|
||||
{OpVariable, state.ptr_func_uint, state.dispatch_pc_variable, StorageClassFunction});
|
||||
}
|
||||
for (const auto& binding: state.registers) {
|
||||
state.builder.AddFunction(
|
||||
{OpVariable, state.ptr_func_uint, binding.pointer_id, StorageClassFunction});
|
||||
}
|
||||
for (const auto* block: program.blocks) {
|
||||
for (const auto& inst: *block) {
|
||||
if (inst.GetOpcode() != IR::ValueOpcode::Phi) {
|
||||
@@ -540,10 +540,6 @@ bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std:
|
||||
state.builder.AddFunction(
|
||||
{OpVariable, state.ptr_func_uint, ctx.scratch_u32_variable, StorageClassFunction});
|
||||
}
|
||||
for (const auto& binding: state.registers) {
|
||||
state.builder.AddFunction({OpStore, binding.pointer_id,
|
||||
ConstantU32(state, InitialRegisterValue(state, binding.reg))});
|
||||
}
|
||||
if (state.pixel_valid_mask_variable != 0) {
|
||||
state.builder.AddFunction(
|
||||
{OpStore, state.pixel_valid_mask_variable, ConstantU32(state, 1)});
|
||||
|
||||
@@ -805,7 +805,7 @@ bool TranslateProgram(const IR::Program& source, IR::ValueProgram& result,
|
||||
return block.id;
|
||||
})->id;
|
||||
if (max_id == UINT32_MAX) {
|
||||
return Fail(error, "cannot allocate a typed prologue block id");
|
||||
return Fail(error, "cannot allocate a typed entry block id");
|
||||
}
|
||||
CFG::Terminator terminator;
|
||||
terminator.kind = CFG::TerminatorKind::Branch;
|
||||
@@ -831,47 +831,35 @@ bool TranslateProgram(const IR::Program& source, IR::ValueProgram& result,
|
||||
}
|
||||
if (!result.blocks.empty()) {
|
||||
result.blocks.front()->AddBranch(result.blocks[1]);
|
||||
IR::IREmitter prologue(result.blocks.front());
|
||||
IR::IREmitter entry_ir(result.blocks.front());
|
||||
const auto builtin = [&](IR::StageInputKind kind, uint32_t component = 0u) {
|
||||
return IR::U32(
|
||||
prologue.Emit(IR::ValueOpcode::GetBuiltin,
|
||||
entry_ir.Emit(IR::ValueOpcode::GetBuiltin,
|
||||
{IR::Value(static_cast<uint32_t>(kind)), IR::Value(component)}));
|
||||
};
|
||||
prologue.Emit(IR::ValueOpcode::Prologue);
|
||||
const IR::U32 zero(IR::Value(0u));
|
||||
for (uint32_t index = 0; index < IR::NumScalarRegs; index++) {
|
||||
prologue.SetScalarReg(static_cast<IR::ScalarReg>(index), zero);
|
||||
}
|
||||
for (uint32_t index = 0; index < vector_limit; index++) {
|
||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(index), zero);
|
||||
}
|
||||
for (uint32_t index = 0; index < source.user_data_count; index++) {
|
||||
const auto reg = static_cast<IR::ScalarReg>(source.user_data_base + index);
|
||||
if (IR::RegIndex(reg) >= IR::NumScalarRegs) {
|
||||
break;
|
||||
}
|
||||
prologue.SetScalarReg(reg, prologue.GetUserData(reg));
|
||||
entry_ir.SetScalarReg(reg, entry_ir.GetUserData(reg));
|
||||
}
|
||||
prologue.SetScc(IR::U1(IR::Value(false)));
|
||||
prologue.SetExec(IR::U1(IR::Value(true)));
|
||||
prologue.SetExecLo(IR::U32(IR::Value(0xffffffffu)));
|
||||
prologue.SetExecHi(IR::U32(IR::Value(source.wave_size > 32u ? 0xffffffffu : 0u)));
|
||||
prologue.SetVcc(IR::U1(IR::Value(false)));
|
||||
prologue.SetVccLo(IR::U32(IR::Value(0u)));
|
||||
prologue.SetVccHi(IR::U32(IR::Value(0u)));
|
||||
prologue.SetM0(IR::U32(IR::Value(0u)));
|
||||
entry_ir.SetExec(IR::U1(IR::Value(true)));
|
||||
entry_ir.SetExecLo(IR::U32(IR::Value(per_invocation_masks ? 1u : 0xffffffffu)));
|
||||
entry_ir.SetExecHi(IR::U32(IR::Value(
|
||||
!per_invocation_masks && source.wave_size > 32u ? 0xffffffffu : 0u)));
|
||||
if (source.stage == ShaderType::Compute) {
|
||||
const auto* cs = compute_input_info;
|
||||
const auto thread_ids =
|
||||
cs->thread_ids_num > 0 ? std::min<uint32_t>(cs->thread_ids_num, 3u) : 0u;
|
||||
for (uint32_t index = 0; index < thread_ids; index++) {
|
||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(index),
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(index),
|
||||
builtin(IR::StageInputKind::LocalInvocationId, index));
|
||||
}
|
||||
uint32_t reg_offset = 0;
|
||||
for (uint32_t index = 0; index < 3u; index++) {
|
||||
if (cs->group_id[index]) {
|
||||
prologue.SetScalarReg(
|
||||
entry_ir.SetScalarReg(
|
||||
static_cast<IR::ScalarReg>(cs->workgroup_register + reg_offset++),
|
||||
builtin(IR::StageInputKind::WorkgroupId, index));
|
||||
}
|
||||
@@ -884,43 +872,43 @@ bool TranslateProgram(const IR::Program& source, IR::ValueProgram& result,
|
||||
const auto waves = std::min((total_threads + wave_size - 1u) / wave_size, 0x3fu);
|
||||
const auto local_index = builtin(IR::StageInputKind::LocalInvocationIndex);
|
||||
const auto wave_id = IR::U32(
|
||||
prologue.Emit(IR::ValueOpcode::UDiv32, {local_index, IR::Value(wave_size)}));
|
||||
const auto wave_bits = prologue.ShiftLeftLogical(wave_id, IR::U32(IR::Value(20u)));
|
||||
entry_ir.Emit(IR::ValueOpcode::UDiv32, {local_index, IR::Value(wave_size)}));
|
||||
const auto wave_bits = entry_ir.ShiftLeftLogical(wave_id, IR::U32(IR::Value(20u)));
|
||||
const auto first_bit =
|
||||
prologue.Select(prologue.IEqual(wave_id, IR::U32(IR::Value(0u))),
|
||||
entry_ir.Select(entry_ir.IEqual(wave_id, IR::U32(IR::Value(0u))),
|
||||
IR::U32(IR::Value(0x80000000u)), IR::U32(IR::Value(0u)));
|
||||
prologue.SetScalarReg(
|
||||
entry_ir.SetScalarReg(
|
||||
static_cast<IR::ScalarReg>(cs->workgroup_register + reg_offset),
|
||||
prologue.BitwiseOr(prologue.BitwiseOr(wave_bits, IR::U32(IR::Value(waves))),
|
||||
entry_ir.BitwiseOr(entry_ir.BitwiseOr(wave_bits, IR::U32(IR::Value(waves))),
|
||||
first_bit));
|
||||
}
|
||||
} else if (source.stage == ShaderType::Pixel) {
|
||||
const auto* ps = pixel_input_info;
|
||||
uint32_t reg = ps->ps_system_input_base;
|
||||
if (ps->ps_pos_x) {
|
||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(reg++),
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg++),
|
||||
builtin(IR::StageInputKind::FragCoord, 0));
|
||||
}
|
||||
if (ps->ps_pos_y) {
|
||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(reg++),
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg++),
|
||||
builtin(IR::StageInputKind::FragCoord, 1));
|
||||
}
|
||||
if (ps->ps_pos_z) {
|
||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(reg++),
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg++),
|
||||
builtin(IR::StageInputKind::FragCoord, 2));
|
||||
}
|
||||
if (ps->ps_pos_w) {
|
||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(reg++),
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg++),
|
||||
builtin(IR::StageInputKind::FragCoord, 3));
|
||||
}
|
||||
if (ps->ps_front_face) {
|
||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(reg),
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg),
|
||||
builtin(IR::StageInputKind::FrontFacing));
|
||||
}
|
||||
} else if (source.stage == ShaderType::Vertex) {
|
||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(5),
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(5),
|
||||
builtin(IR::StageInputKind::VertexIndex));
|
||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(8),
|
||||
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(8),
|
||||
builtin(IR::StageInputKind::InstanceIndex));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,7 @@ namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
enum class ScalarReg : uint16_t {};
|
||||
enum class VectorReg : uint16_t {};
|
||||
|
||||
// Architectural SGPRs occupy the low range. The current shader-only migration also accepts the
|
||||
// scalar shadow registers produced by Kyty's pre-existing read-lane pass; that pass disappears
|
||||
// once read-lane elimination operates on the SSA graph.
|
||||
constexpr uint32_t NumScalarRegs = 512;
|
||||
constexpr uint32_t NumScalarRegs = 106;
|
||||
constexpr uint32_t NumVectorRegs = 256;
|
||||
|
||||
constexpr uint32_t RegIndex(ScalarReg reg) {
|
||||
|
||||
@@ -12,20 +12,32 @@ using ShaderError::Fail;
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsRegisterGet(ValueOpcode opcode) {
|
||||
bool IsRegisterStatePseudo(ValueOpcode opcode) {
|
||||
switch (opcode) {
|
||||
case ValueOpcode::GetThreadBitScalarRegister:
|
||||
case ValueOpcode::SetThreadBitScalarRegister:
|
||||
case ValueOpcode::GetScalarRegister:
|
||||
case ValueOpcode::SetScalarRegister:
|
||||
case ValueOpcode::GetVectorRegister:
|
||||
case ValueOpcode::SetVectorRegister:
|
||||
case ValueOpcode::GetGotoVariable:
|
||||
case ValueOpcode::SetGotoVariable:
|
||||
case ValueOpcode::GetScc:
|
||||
case ValueOpcode::SetScc:
|
||||
case ValueOpcode::GetExec:
|
||||
case ValueOpcode::SetExec:
|
||||
case ValueOpcode::GetExecLo:
|
||||
case ValueOpcode::SetExecLo:
|
||||
case ValueOpcode::GetExecHi:
|
||||
case ValueOpcode::SetExecHi:
|
||||
case ValueOpcode::GetVcc:
|
||||
case ValueOpcode::SetVcc:
|
||||
case ValueOpcode::GetVccLo:
|
||||
case ValueOpcode::SetVccLo:
|
||||
case ValueOpcode::GetVccHi:
|
||||
case ValueOpcode::GetM0: return true;
|
||||
case ValueOpcode::SetVccHi:
|
||||
case ValueOpcode::GetM0:
|
||||
case ValueOpcode::SetM0: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -137,8 +149,8 @@ bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::st
|
||||
if (inst.Parent() != block) {
|
||||
return Fail(error, "value IR instruction has the wrong parent block");
|
||||
}
|
||||
if (require_ssa && IsRegisterGet(inst.GetOpcode())) {
|
||||
return Fail(error, fmt::format("register getter {} survived SSA rewrite",
|
||||
if (require_ssa && IsRegisterStatePseudo(inst.GetOpcode())) {
|
||||
return Fail(error, fmt::format("register-state pseudo {} survived SSA rewrite",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
if (inst.GetOpcode() != ValueOpcode::Phi && inst.GetOpcode() != ValueOpcode::Identity &&
|
||||
|
||||
@@ -74,8 +74,6 @@ bool HasSideEffects(ValueOpcode opcode) {
|
||||
switch (opcode) {
|
||||
case ValueOpcode::Reference:
|
||||
case ValueOpcode::ReferenceU32:
|
||||
case ValueOpcode::Prologue:
|
||||
case ValueOpcode::Epilogue:
|
||||
case ValueOpcode::StoreAddressU8:
|
||||
case ValueOpcode::StoreAddressU16:
|
||||
case ValueOpcode::StoreAddressU32:
|
||||
|
||||
@@ -7,8 +7,6 @@ VALUE_OPCODE(Reference, Void, U1)
|
||||
VALUE_OPCODE(ReferenceU32, Void, U32)
|
||||
|
||||
// Translation and register-state pseudo operations.
|
||||
VALUE_OPCODE(Prologue, Void)
|
||||
VALUE_OPCODE(Epilogue, Void)
|
||||
VALUE_OPCODE(GetUserData, U32, ScalarReg)
|
||||
VALUE_OPCODE(GetShaderBase, U64)
|
||||
VALUE_OPCODE(GetBuiltin, U32, U32, U32)
|
||||
|
||||
+144
-108
@@ -15,6 +15,7 @@
|
||||
#include "graphics/shader/recompiler/frontend/cfg/ShaderCFG.h"
|
||||
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
|
||||
#include "graphics/shader/recompiler/frontend/translate/Translate.h"
|
||||
#include "graphics/shader/recompiler/ir/IREmitter.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
#include "graphics/shader/recompiler/ir/ValueProgram.h"
|
||||
#include "graphics/shader/recompiler/ir/passes/ConstantPropagation.h"
|
||||
@@ -7506,93 +7507,6 @@ void TestComputeShaderInputWaveSize() {
|
||||
"cleared COMPUTE_PGM_RSRC1 W32_EN bit did not decode as wave64");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerWave32MasksExecHighStores() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeSop1(0x04, 126, 193), // s_mov_b64 exec, -1
|
||||
EncodeSopp(0x01),
|
||||
};
|
||||
|
||||
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
options.dump_ir = true;
|
||||
options.wave_size = 32;
|
||||
|
||||
ShaderRecompiler::CompileResult result;
|
||||
std::string error;
|
||||
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error),
|
||||
error.c_str());
|
||||
Check(Common::ContainsStr(result.decoded_dump, "s_mov_b64 exec_lo, -1"),
|
||||
"wave32 EXEC mask regression did not decode s_mov_b64 exec, -1");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(CountSourceOccurrences(source, "OpStore %exec_hi %uint_0") >= 2u,
|
||||
"wave32 EXEC high half was not clamped to zero in SPIR-V stores");
|
||||
Check(!Common::ContainsStr(source, "OpStore %exec_hi %uint_4294967295"),
|
||||
"wave32 EXEC high half was stored as a full 64-lane mask");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerWave32VccHighScalarStores() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeSmem0(0x09, 106, 4),
|
||||
0u, // s_buffer_load_dwordx2 vcc_lo
|
||||
EncodeSopp(0x01),
|
||||
};
|
||||
|
||||
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
options.dump_ir = true;
|
||||
options.wave_size = 32;
|
||||
|
||||
ShaderRecompiler::CompileResult result;
|
||||
std::string error;
|
||||
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error),
|
||||
error.c_str());
|
||||
Check(Common::ContainsStr(result.ir_dump, "SBufferLoadDword vcc_hi"),
|
||||
"wave32 VCC scalar regression did not lower the high VCC dword load");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(CountSourceOccurrences(source, "OpStore %vcc_hi") >= 2u,
|
||||
"wave32 VCC high scalar load did not store to vcc_hi");
|
||||
Check(CountSourceOccurrences(source, "OpStore %vcc_hi %uint_0") == 1u,
|
||||
"wave32 VCC high scalar load was clamped to zero instead of preserving "
|
||||
"data");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerCompareMaskIsFullWaveBallot() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeVopc(0xc1, 0 + 256, 1), // v_cmp_lt_u32 vcc, v0, v1
|
||||
EncodeSop2(0x0f, 2, 126, 106), // s_and_b64 s[2:3], exec, vcc
|
||||
EncodeSop1(0x04, 126, 2), // s_mov_b64 exec, s[2:3]
|
||||
EncodeSopp(0x01),
|
||||
};
|
||||
|
||||
auto options = MakeCompileOptions(ShaderType::Compute);
|
||||
options.dump_ir = true;
|
||||
options.wave_size = 32;
|
||||
|
||||
ShaderRecompiler::CompileResult result;
|
||||
std::string error;
|
||||
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error),
|
||||
error.c_str());
|
||||
Check(Common::ContainsStr(result.decoded_dump, "v_cmp_lt_u32"),
|
||||
"compare-mask ballot regression did not decode V_CMP_LT_U32");
|
||||
Check(Common::ContainsStr(result.ir_dump, "CompareLtU32 vcc_lo"),
|
||||
"compare-mask ballot regression did not lower compare to VCC");
|
||||
Check(
|
||||
Common::ContainsStr(result.ir_dump, "BitwiseAndU64 s2, exec_lo, vcc_lo"),
|
||||
"compare-mask ballot regression did not consume VCC through scalar mask "
|
||||
"ALU");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(Common::ContainsStr(source, "OpGroupNonUniformBallot"),
|
||||
"compare-mask result was not materialized with a subgroup ballot");
|
||||
Check(Common::ContainsStr(source, "OpStore %vcc_lo"),
|
||||
"compare-mask ballot regression did not store VCC low mask");
|
||||
Check(Common::ContainsStr(source, "OpStore %vcc_hi %uint_0"),
|
||||
"wave32 compare-mask VCC high half was not cleared");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerBufferLoadsGuardedByExec() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeMubuf0(0x0c),
|
||||
@@ -7885,9 +7799,119 @@ void TestNewShaderRecompilerExpVertexOutputs() {
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
}
|
||||
|
||||
void TestTypedEntryStateIsMinimal() {
|
||||
using namespace ShaderRecompiler;
|
||||
|
||||
const auto translate = [](ShaderLaneMaskMode lane_mode, uint32_t wave_size) {
|
||||
IR::Program source;
|
||||
source.stage = ShaderType::Compute;
|
||||
source.lane_mask_mode = lane_mode;
|
||||
source.wave_size = wave_size;
|
||||
source.user_data_count = 0;
|
||||
source.blocks.emplace_back();
|
||||
source.blocks.front().id = 0;
|
||||
source.blocks.front().terminator.kind = CFG::TerminatorKind::Return;
|
||||
|
||||
IR::ValueProgram values;
|
||||
ShaderComputeInputInfo compute{};
|
||||
std::string error;
|
||||
Check(Frontend::TranslateProgram(source, values, nullptr, nullptr, &compute,
|
||||
&error),
|
||||
error.c_str());
|
||||
return values;
|
||||
};
|
||||
|
||||
const auto check = [&](ShaderLaneMaskMode lane_mode, uint32_t wave_size) {
|
||||
auto values = translate(lane_mode, wave_size);
|
||||
uint32_t set_exec = 0;
|
||||
uint32_t set_exec_lo = 0;
|
||||
uint32_t set_exec_hi = 0;
|
||||
IR::Value exec;
|
||||
IR::Value exec_lo;
|
||||
IR::Value exec_hi;
|
||||
for (const auto *block : values.blocks) {
|
||||
for (const auto &inst : *block) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case IR::ValueOpcode::SetExec:
|
||||
set_exec++;
|
||||
exec = inst.Arg(0).Resolve();
|
||||
break;
|
||||
case IR::ValueOpcode::SetExecLo:
|
||||
set_exec_lo++;
|
||||
exec_lo = inst.Arg(0).Resolve();
|
||||
break;
|
||||
case IR::ValueOpcode::SetExecHi:
|
||||
set_exec_hi++;
|
||||
exec_hi = inst.Arg(0).Resolve();
|
||||
break;
|
||||
case IR::ValueOpcode::SetScalarRegister:
|
||||
case IR::ValueOpcode::SetThreadBitScalarRegister:
|
||||
case IR::ValueOpcode::SetVectorRegister:
|
||||
case IR::ValueOpcode::SetScc:
|
||||
case IR::ValueOpcode::SetVcc:
|
||||
case IR::ValueOpcode::SetVccLo:
|
||||
case IR::ValueOpcode::SetVccHi:
|
||||
case IR::ValueOpcode::SetM0:
|
||||
Check(false, "minimal typed entry contains redundant state writes");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Check(set_exec == 1u && set_exec_lo == 1u && set_exec_hi == 1u &&
|
||||
exec.IsImmediate() && exec.U1(),
|
||||
"typed entry does not define EXEC exactly once");
|
||||
if (lane_mode == ShaderLaneMaskMode::PerInvocation) {
|
||||
Check(exec_lo.IsImmediate() && exec_lo.U32() == 1u &&
|
||||
exec_hi.IsImmediate() && exec_hi.U32() == 0u,
|
||||
"per-invocation entry EXEC is not the local {1,0} mask");
|
||||
} else {
|
||||
Check(exec_lo.IsImmediate() && exec_lo.U32() == 0xffffffffu &&
|
||||
exec_hi.IsImmediate() &&
|
||||
exec_hi.U32() == (wave_size == 32u ? 0u : 0xffffffffu),
|
||||
"native entry EXEC does not match the guest wave width");
|
||||
}
|
||||
|
||||
IR::RewriteToSsa(values.blocks);
|
||||
IR::RemoveIdentities(values.blocks);
|
||||
IR::EliminateDeadCode(values.blocks);
|
||||
std::string error;
|
||||
Check(IR::ValidateValueProgram(values, true, &error), error.c_str());
|
||||
};
|
||||
|
||||
check(ShaderLaneMaskMode::NativeWave, 32u);
|
||||
check(ShaderLaneMaskMode::NativeWave, 64u);
|
||||
check(ShaderLaneMaskMode::PerInvocation, 64u);
|
||||
}
|
||||
|
||||
void TestFinalSsaRejectsRegisterStatePseudos() {
|
||||
using namespace ShaderRecompiler::IR;
|
||||
|
||||
const auto check_rejected = [](bool setter) {
|
||||
ValueProgram program;
|
||||
program.block_storage.push_back(std::make_unique<Block>());
|
||||
program.blocks.push_back(program.block_storage.back().get());
|
||||
program.block_info.emplace_back();
|
||||
IREmitter ir(program.blocks.front());
|
||||
if (setter) {
|
||||
ir.SetScalarReg(ScalarReg{0}, U32(Value(1u)));
|
||||
} else {
|
||||
ir.Emit(ValueOpcode::ReferenceU32, {ir.GetScalarReg(ScalarReg{0})});
|
||||
}
|
||||
std::string error;
|
||||
Check(!ValidateValueProgram(program, true, &error) &&
|
||||
Common::ContainsStr(error, "register-state pseudo"),
|
||||
"final SSA accepted a register-state pseudo operation");
|
||||
};
|
||||
|
||||
check_rejected(false);
|
||||
check_rejected(true);
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerZeroInitialRegisterState() {
|
||||
const uint32_t shader[] = {
|
||||
EncodeVop1(0x01, 0, 0), // v_mov_b32 v0, s0
|
||||
EncodeVop1(0x01, 0, 100), // v_mov_b32 v0, s100
|
||||
EncodeExp0(0x0c, 0xf),
|
||||
EncodeExp1(0, 1, 2, 3), // POS0
|
||||
EncodeSopp(0x01),
|
||||
@@ -7901,14 +7925,31 @@ void TestNewShaderRecompilerZeroInitialRegisterState() {
|
||||
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error),
|
||||
error.c_str());
|
||||
Check(!Common::ContainsStr(result.ir_dump, "UndefU32"),
|
||||
"typed prologue left guest registers undefined");
|
||||
"SSA initial state left guest registers undefined");
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
Check(!Common::ContainsStr(source, "OpUndef"),
|
||||
"zero-initialized guest registers became SPIR-V undef values");
|
||||
Check(!Common::ContainsStr(source, "%s100") &&
|
||||
!Common::ContainsStr(source, "%v0"),
|
||||
"final SPIR-V retained a guest register mirror");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
const uint32_t explicit_zero_shader[] = {
|
||||
EncodeSMovB32(100, 128), // s_mov_b32 s100, 0
|
||||
EncodeVop1(0x01, 0, 100), // v_mov_b32 v0, s100
|
||||
EncodeExp0(0x0c, 0xf),
|
||||
EncodeExp1(0, 1, 2, 3), // POS0
|
||||
EncodeSopp(0x01),
|
||||
};
|
||||
ShaderRecompiler::CompileResult explicit_zero;
|
||||
Check(ShaderRecompiler::TryRecompile(explicit_zero_shader, options,
|
||||
explicit_zero, &error),
|
||||
error.c_str());
|
||||
Check(result.spirv == explicit_zero.spirv,
|
||||
"implicit and explicit zero register state emitted different SPIR-V");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerVertexSystemVgprs() {
|
||||
void TestNewShaderRecompilerVertexSystemInputsWithoutMirrors() {
|
||||
using StageInputKind = ShaderRecompiler::IR::StageInputKind;
|
||||
|
||||
const uint32_t shader[] = {
|
||||
@@ -7932,10 +7973,6 @@ void TestNewShaderRecompilerVertexSystemVgprs() {
|
||||
"vertex shader missing VertexIndex input");
|
||||
Check(ProgramHasInput(result.program, StageInputKind::InstanceIndex),
|
||||
"vertex shader missing InstanceIndex input");
|
||||
Check(Common::ContainsStr(result.ir_dump, "MoveU32 v0, v5"),
|
||||
"vertex shader did not keep v5 as a guest VGPR source");
|
||||
Check(Common::ContainsStr(result.ir_dump, "MoveU32 v1, v8"),
|
||||
"vertex shader did not keep v8 as a guest VGPR source");
|
||||
CheckSpirvBinaryValidates(result.spirv);
|
||||
|
||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||
@@ -7943,10 +7980,9 @@ void TestNewShaderRecompilerVertexSystemVgprs() {
|
||||
"vertex SPIR-V does not load gl_VertexIndex");
|
||||
Check(CountSourceOccurrences(source, "OpLoad %int %gl_InstanceIndex") == 1u,
|
||||
"vertex SPIR-V does not load gl_InstanceIndex");
|
||||
Check(CountSourceOccurrences(source, "OpStore %v5") >= 2u,
|
||||
"vertex SPIR-V does not seed guest v5 from gl_VertexIndex");
|
||||
Check(CountSourceOccurrences(source, "OpStore %v8") >= 2u,
|
||||
"vertex SPIR-V does not seed guest v8 from gl_InstanceIndex");
|
||||
Check(!Common::ContainsStr(source, "%v5") &&
|
||||
!Common::ContainsStr(source, "%v8"),
|
||||
"vertex system values were routed through guest VGPR mirrors");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerVertexExportUsesLaneExecMask() {
|
||||
@@ -7988,9 +8024,9 @@ void TestNewShaderRecompilerVertexExportUsesLaneExecMask() {
|
||||
"per-invocation vertex export lost its EXEC guard");
|
||||
}
|
||||
|
||||
void TestNewShaderRecompilerPerInvocationMasks() {
|
||||
void TestNewShaderRecompilerPerInvocationMasksWithoutMirrors() {
|
||||
const uint32_t local_shader[] = {
|
||||
EncodeVopc(0xc1, 0 + 256, 1), // v_cmp_lt_u32 vcc, v0, v1
|
||||
EncodeVopc(0xc1, 5 + 256, 8), // v_cmp_lt_u32 vcc, v5, v8
|
||||
EncodeSop2(0x0f, 2, 126, 106), // s_and_b64 s[2:3], exec, vcc
|
||||
EncodeSop1(0x24, 4, 126), // s_and_saveexec_b64 s[4:5], exec
|
||||
EncodeSop2(0x25, 126, 132, 128), // s_bfm_b64 exec, 4, 0
|
||||
@@ -8016,17 +8052,13 @@ void TestNewShaderRecompilerPerInvocationMasks() {
|
||||
"per-invocation VCC producer still materialized a shared subgroup mask");
|
||||
Check(!Common::ContainsStr(source, "OpLoad %uint %gl_SubgroupInvocationID"),
|
||||
"per-invocation BFM EXEC prefix still selected native subgroup lanes");
|
||||
Check(Common::ContainsStr(source, "OpLogicalAnd") &&
|
||||
Common::ContainsStr(source, "OpLogicalNot"),
|
||||
"per-invocation wide mask ALU did not use Boolean operations");
|
||||
Check(Common::ContainsStr(source, "OpStore %vcc_lo") &&
|
||||
Common::ContainsStr(source, "OpStore %vcc_hi %uint_0"),
|
||||
"per-invocation comparison did not store a local Boolean VCC pair");
|
||||
Check(Common::ContainsStr(result.ir_dump, "SaveexecB64 s4, exec_lo"),
|
||||
"per-invocation SAVEEXEC regression did not reach IR");
|
||||
Check(!Common::ContainsStr(source, "%vcc_lo") &&
|
||||
!Common::ContainsStr(source, "%vcc_hi"),
|
||||
"per-invocation comparison retained VCC register mirrors");
|
||||
|
||||
const uint32_t wqm_shader[] = {
|
||||
EncodeSop1(0x0a, 2, 126), // s_wqm_b64 s[2:3], exec
|
||||
EncodeVop1(0x01, 0, 2), // v_mov_b32 v0, s2
|
||||
EncodeExp0(0x0c, 0xf),
|
||||
EncodeExp1(0, 1, 2, 3), // POS0
|
||||
EncodeSopp(0x01),
|
||||
@@ -9760,8 +9792,12 @@ int main() {
|
||||
TestNewShaderRecompilerSetpcBranch();
|
||||
TestNewShaderRecompilerSetpcJumpTable();
|
||||
TestNewShaderRecompilerSetpcDwordJumpTable();
|
||||
TestTypedEntryStateIsMinimal();
|
||||
TestFinalSsaRejectsRegisterStatePseudos();
|
||||
TestNewShaderRecompilerZeroInitialRegisterState();
|
||||
TestNewShaderRecompilerVertexSystemInputsWithoutMirrors();
|
||||
TestNewShaderRecompilerVertexExportUsesLaneExecMask();
|
||||
TestNewShaderRecompilerPerInvocationMasksWithoutMirrors();
|
||||
TestNewShaderRecompilerPerInvocationU64Complement();
|
||||
TestNewShaderRecompilerExpPixelOutputs();
|
||||
TestRenderTargetReverseExportMapping();
|
||||
|
||||
Reference in New Issue
Block a user