mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +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);
|
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) {
|
IR::SpirvRequirements GetProgramRequirements(const IR::Program& program) {
|
||||||
if (program.spirv_requirements.has_value()) {
|
if (program.spirv_requirements.has_value()) {
|
||||||
return *program.spirv_requirements;
|
return *program.spirv_requirements;
|
||||||
@@ -418,14 +373,9 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
|
|||||||
state.stage = program.stage;
|
state.stage = program.stage;
|
||||||
state.wave_size = program.wave_size;
|
state.wave_size = program.wave_size;
|
||||||
state.per_invocation_masks = program.lane_mask_mode == ShaderLaneMaskMode::PerInvocation;
|
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.inputs.reserve(InitialEmitterVectorReserve);
|
||||||
state.outputs.reserve(InitialEmitterVectorReserve);
|
state.outputs.reserve(InitialEmitterVectorReserve);
|
||||||
state.interface_variables.reserve(InitialEmitterVectorReserve);
|
state.interface_variables.reserve(InitialEmitterVectorReserve);
|
||||||
state.reachable_blocks.reserve(InitialEmitterVectorReserve);
|
|
||||||
CollectValueRegisterMirrors(value_program, state.registers);
|
|
||||||
CopyProgramInputsAndOutputs(state, program);
|
CopyProgramInputsAndOutputs(state, program);
|
||||||
state.needs_subgroup_ballot = requirements.subgroup_ballot;
|
state.needs_subgroup_ballot = requirements.subgroup_ballot;
|
||||||
state.needs_subgroup_shuffle = requirements.subgroup_shuffle;
|
state.needs_subgroup_shuffle = requirements.subgroup_shuffle;
|
||||||
@@ -438,16 +388,13 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
|
|||||||
AllocateOutputVariables(state);
|
AllocateOutputVariables(state);
|
||||||
AllocateDescriptorVariables(state);
|
AllocateDescriptorVariables(state);
|
||||||
EmitHeaderAndTypes(state);
|
EmitHeaderAndTypes(state);
|
||||||
AllocateRegisterVariables(state);
|
|
||||||
if (!EmitValueProgram(state, value_program, error)) {
|
if (!EmitValueProgram(state, value_program, error)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReportReserveExceeded(program, "registers", state.registers.size());
|
|
||||||
ReportReserveExceeded(program, "inputs", state.inputs.size());
|
ReportReserveExceeded(program, "inputs", state.inputs.size());
|
||||||
ReportReserveExceeded(program, "outputs", state.outputs.size());
|
ReportReserveExceeded(program, "outputs", state.outputs.size());
|
||||||
ReportReserveExceeded(program, "interface_variables", state.interface_variables.size());
|
ReportReserveExceeded(program, "interface_variables", state.interface_variables.size());
|
||||||
ReportReserveExceeded(program, "reachable_blocks", state.reachable_blocks.size());
|
|
||||||
|
|
||||||
auto binary = state.builder.Build();
|
auto binary = state.builder.Build();
|
||||||
if (binary.empty()) {
|
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,
|
bool HasOutput(const std::vector<OutputBinding>& outputs, IR::StageOutputKind kind,
|
||||||
uint32_t index) {
|
uint32_t index) {
|
||||||
return std::any_of(outputs.begin(), outputs.end(), [kind, index](const OutputBinding& binding) {
|
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;
|
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);
|
uint32_t ConstantU32(EmitterState& state, uint32_t value);
|
||||||
[[noreturn]] void ExitDescriptorBindingFailure(const EmitterState& state,
|
[[noreturn]] void ExitDescriptorBindingFailure(const EmitterState& state,
|
||||||
IR::DescriptorBindingKind kind, uint32_t resource,
|
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 Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
||||||
namespace {
|
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 EmitWqmLaneU32(EmitterState& state, uint32_t src) {
|
||||||
uint32_t ret = ConstantU32(state, 0);
|
uint32_t ret = ConstantU32(state, 0);
|
||||||
for (uint32_t i = 0; i < 8u; i++) {
|
for (uint32_t i = 0; i < 8u; i++) {
|
||||||
@@ -32,19 +45,6 @@ uint32_t BoolAsU32(ValueEmitContext& ctx, uint32_t value) {
|
|||||||
return result;
|
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) {
|
uint32_t EmitBuiltinU32(ValueEmitContext& ctx, IR::StageInputKind kind, uint32_t component) {
|
||||||
auto& state = ctx.state;
|
auto& state = ctx.state;
|
||||||
if (kind == IR::StageInputKind::LocalInvocationIndex) {
|
if (kind == IR::StageInputKind::LocalInvocationIndex) {
|
||||||
@@ -338,61 +338,9 @@ bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst) {
|
|||||||
switch (inst.GetOpcode()) {
|
switch (inst.GetOpcode()) {
|
||||||
case IR::ValueOpcode::Identity:
|
case IR::ValueOpcode::Identity:
|
||||||
case IR::ValueOpcode::ConditionRef: ctx.Define(inst, ctx.Arg(inst, 0)); return true;
|
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::Void:
|
||||||
case IR::ValueOpcode::Reference:
|
case IR::ValueOpcode::Reference:
|
||||||
case IR::ValueOpcode::ReferenceU32:
|
case IR::ValueOpcode::ReferenceU32:
|
||||||
case IR::ValueOpcode::Prologue:
|
|
||||||
case IR::ValueOpcode::Epilogue:
|
|
||||||
case IR::ValueOpcode::ControlNop:
|
case IR::ValueOpcode::ControlNop:
|
||||||
case IR::ValueOpcode::Waitcnt:
|
case IR::ValueOpcode::Waitcnt:
|
||||||
case IR::ValueOpcode::Sendmsg:
|
case IR::ValueOpcode::Sendmsg:
|
||||||
|
|||||||
@@ -273,11 +273,6 @@ enum : uint32_t {
|
|||||||
GlslFindUMsb = 75,
|
GlslFindUMsb = 75,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RegisterBinding {
|
|
||||||
IR::Register reg;
|
|
||||||
uint32_t pointer_id = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InputBinding {
|
struct InputBinding {
|
||||||
IR::StageInputKind kind = IR::StageInputKind::VertexIndex;
|
IR::StageInputKind kind = IR::StageInputKind::VertexIndex;
|
||||||
uint32_t location = 0;
|
uint32_t location = 0;
|
||||||
@@ -327,7 +322,6 @@ struct EmitterState {
|
|||||||
ShaderStageInputInfo input_info;
|
ShaderStageInputInfo input_info;
|
||||||
ShaderType stage = ShaderType::Unknown;
|
ShaderType stage = ShaderType::Unknown;
|
||||||
uint32_t wave_size = 64;
|
uint32_t wave_size = 64;
|
||||||
bool exact_subgroup_operations = false;
|
|
||||||
bool per_invocation_masks = false;
|
bool per_invocation_masks = false;
|
||||||
uint32_t void_type = 0;
|
uint32_t void_type = 0;
|
||||||
uint32_t bool_type = 0;
|
uint32_t bool_type = 0;
|
||||||
@@ -426,15 +420,9 @@ struct EmitterState {
|
|||||||
bool needs_image_gather_extended = false;
|
bool needs_image_gather_extended = false;
|
||||||
bool needs_function_lds = false;
|
bool needs_function_lds = false;
|
||||||
bool needs_pixel_valid_mask = 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<InputBinding> inputs;
|
||||||
std::vector<OutputBinding> outputs;
|
std::vector<OutputBinding> outputs;
|
||||||
std::vector<uint32_t> interface_variables;
|
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> constants;
|
||||||
std::map<uint32_t, uint32_t> signed_constants;
|
std::map<uint32_t, uint32_t> signed_constants;
|
||||||
std::map<uint32_t, uint32_t> float_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 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);
|
bool HasOutput(const std::vector<OutputBinding>& outputs, IR::StageOutputKind kind, uint32_t index);
|
||||||
|
|
||||||
void CopyProgramInputsAndOutputs(EmitterState& state, const IR::Program& program);
|
void CopyProgramInputsAndOutputs(EmitterState& state, const IR::Program& program);
|
||||||
|
|
||||||
uint32_t OutputVariableForExport(const EmitterState& state, const IR::ExportInfo& exp);
|
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 ConstantU32(EmitterState& state, uint32_t value);
|
||||||
|
|
||||||
uint32_t EmitSubgroupLocalInvocationId(EmitterState& state);
|
uint32_t EmitSubgroupLocalInvocationId(EmitterState& state);
|
||||||
@@ -714,8 +696,6 @@ void AddDescriptorAnnotationsAndNames(EmitterState& state);
|
|||||||
|
|
||||||
void EmitHeaderAndTypes(EmitterState& state);
|
void EmitHeaderAndTypes(EmitterState& state);
|
||||||
|
|
||||||
void AllocateRegisterVariables(EmitterState& state);
|
|
||||||
|
|
||||||
void AllocateDescriptorVariables(EmitterState& state);
|
void AllocateDescriptorVariables(EmitterState& state);
|
||||||
|
|
||||||
uint32_t EmitTrueBool(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);
|
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 StorageBufferPackedStride(const EmitterState& state, const IR::MemoryInfo& mem,
|
||||||
uint32_t use_pc);
|
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 EmitF16BitsToF32(EmitterState& state, uint32_t bits);
|
||||||
|
|
||||||
uint32_t InitialRegisterValue(const EmitterState& state, IR::Register reg);
|
|
||||||
|
|
||||||
bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst);
|
bool EmitValueAlu(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||||
|
|
||||||
bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst);
|
bool EmitValueFlow(ValueEmitContext& ctx, const IR::Inst& inst);
|
||||||
|
|||||||
@@ -2,6 +2,28 @@
|
|||||||
|
|
||||||
namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter {
|
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) {
|
uint32_t EmitAddU32(EmitterState& state, uint32_t lhs, uint32_t rhs) {
|
||||||
const auto ret = state.builder.AllocateId();
|
const auto ret = state.builder.AllocateId();
|
||||||
state.builder.AddFunction({OpIAdd, state.uint_type, ret, lhs, rhs});
|
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});
|
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) {
|
void AllocateDescriptorVariables(EmitterState& state) {
|
||||||
if (DescriptorBinding(state, IR::DescriptorBindingKind::Buffers) != nullptr) {
|
if (DescriptorBinding(state, IR::DescriptorBindingKind::Buffers) != nullptr) {
|
||||||
state.storage_buffer_variable = state.builder.AllocateId();
|
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) {
|
bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std::string* error) {
|
||||||
ValueEmitContext ctx(state, program);
|
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) {
|
for (const auto* block: program.blocks) {
|
||||||
ctx.labels.emplace(block, state.builder.AllocateId());
|
ctx.labels.emplace(block, state.builder.AllocateId());
|
||||||
for (const auto& inst: *block) {
|
for (const auto& inst: *block) {
|
||||||
@@ -512,10 +516,6 @@ bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std:
|
|||||||
state.builder.AddFunction(
|
state.builder.AddFunction(
|
||||||
{OpVariable, state.ptr_func_uint, state.dispatch_pc_variable, StorageClassFunction});
|
{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* block: program.blocks) {
|
||||||
for (const auto& inst: *block) {
|
for (const auto& inst: *block) {
|
||||||
if (inst.GetOpcode() != IR::ValueOpcode::Phi) {
|
if (inst.GetOpcode() != IR::ValueOpcode::Phi) {
|
||||||
@@ -540,10 +540,6 @@ bool EmitValueProgram(EmitterState& state, const IR::ValueProgram& program, std:
|
|||||||
state.builder.AddFunction(
|
state.builder.AddFunction(
|
||||||
{OpVariable, state.ptr_func_uint, ctx.scratch_u32_variable, StorageClassFunction});
|
{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) {
|
if (state.pixel_valid_mask_variable != 0) {
|
||||||
state.builder.AddFunction(
|
state.builder.AddFunction(
|
||||||
{OpStore, state.pixel_valid_mask_variable, ConstantU32(state, 1)});
|
{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;
|
return block.id;
|
||||||
})->id;
|
})->id;
|
||||||
if (max_id == UINT32_MAX) {
|
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;
|
CFG::Terminator terminator;
|
||||||
terminator.kind = CFG::TerminatorKind::Branch;
|
terminator.kind = CFG::TerminatorKind::Branch;
|
||||||
@@ -831,47 +831,35 @@ bool TranslateProgram(const IR::Program& source, IR::ValueProgram& result,
|
|||||||
}
|
}
|
||||||
if (!result.blocks.empty()) {
|
if (!result.blocks.empty()) {
|
||||||
result.blocks.front()->AddBranch(result.blocks[1]);
|
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) {
|
const auto builtin = [&](IR::StageInputKind kind, uint32_t component = 0u) {
|
||||||
return IR::U32(
|
return IR::U32(
|
||||||
prologue.Emit(IR::ValueOpcode::GetBuiltin,
|
entry_ir.Emit(IR::ValueOpcode::GetBuiltin,
|
||||||
{IR::Value(static_cast<uint32_t>(kind)), IR::Value(component)}));
|
{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++) {
|
for (uint32_t index = 0; index < source.user_data_count; index++) {
|
||||||
const auto reg = static_cast<IR::ScalarReg>(source.user_data_base + index);
|
const auto reg = static_cast<IR::ScalarReg>(source.user_data_base + index);
|
||||||
if (IR::RegIndex(reg) >= IR::NumScalarRegs) {
|
if (IR::RegIndex(reg) >= IR::NumScalarRegs) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
prologue.SetScalarReg(reg, prologue.GetUserData(reg));
|
entry_ir.SetScalarReg(reg, entry_ir.GetUserData(reg));
|
||||||
}
|
}
|
||||||
prologue.SetScc(IR::U1(IR::Value(false)));
|
entry_ir.SetExec(IR::U1(IR::Value(true)));
|
||||||
prologue.SetExec(IR::U1(IR::Value(true)));
|
entry_ir.SetExecLo(IR::U32(IR::Value(per_invocation_masks ? 1u : 0xffffffffu)));
|
||||||
prologue.SetExecLo(IR::U32(IR::Value(0xffffffffu)));
|
entry_ir.SetExecHi(IR::U32(IR::Value(
|
||||||
prologue.SetExecHi(IR::U32(IR::Value(source.wave_size > 32u ? 0xffffffffu : 0u)));
|
!per_invocation_masks && 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)));
|
|
||||||
if (source.stage == ShaderType::Compute) {
|
if (source.stage == ShaderType::Compute) {
|
||||||
const auto* cs = compute_input_info;
|
const auto* cs = compute_input_info;
|
||||||
const auto thread_ids =
|
const auto thread_ids =
|
||||||
cs->thread_ids_num > 0 ? std::min<uint32_t>(cs->thread_ids_num, 3u) : 0u;
|
cs->thread_ids_num > 0 ? std::min<uint32_t>(cs->thread_ids_num, 3u) : 0u;
|
||||||
for (uint32_t index = 0; index < thread_ids; index++) {
|
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));
|
builtin(IR::StageInputKind::LocalInvocationId, index));
|
||||||
}
|
}
|
||||||
uint32_t reg_offset = 0;
|
uint32_t reg_offset = 0;
|
||||||
for (uint32_t index = 0; index < 3u; index++) {
|
for (uint32_t index = 0; index < 3u; index++) {
|
||||||
if (cs->group_id[index]) {
|
if (cs->group_id[index]) {
|
||||||
prologue.SetScalarReg(
|
entry_ir.SetScalarReg(
|
||||||
static_cast<IR::ScalarReg>(cs->workgroup_register + reg_offset++),
|
static_cast<IR::ScalarReg>(cs->workgroup_register + reg_offset++),
|
||||||
builtin(IR::StageInputKind::WorkgroupId, index));
|
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 waves = std::min((total_threads + wave_size - 1u) / wave_size, 0x3fu);
|
||||||
const auto local_index = builtin(IR::StageInputKind::LocalInvocationIndex);
|
const auto local_index = builtin(IR::StageInputKind::LocalInvocationIndex);
|
||||||
const auto wave_id = IR::U32(
|
const auto wave_id = IR::U32(
|
||||||
prologue.Emit(IR::ValueOpcode::UDiv32, {local_index, IR::Value(wave_size)}));
|
entry_ir.Emit(IR::ValueOpcode::UDiv32, {local_index, IR::Value(wave_size)}));
|
||||||
const auto wave_bits = prologue.ShiftLeftLogical(wave_id, IR::U32(IR::Value(20u)));
|
const auto wave_bits = entry_ir.ShiftLeftLogical(wave_id, IR::U32(IR::Value(20u)));
|
||||||
const auto first_bit =
|
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)));
|
IR::U32(IR::Value(0x80000000u)), IR::U32(IR::Value(0u)));
|
||||||
prologue.SetScalarReg(
|
entry_ir.SetScalarReg(
|
||||||
static_cast<IR::ScalarReg>(cs->workgroup_register + reg_offset),
|
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));
|
first_bit));
|
||||||
}
|
}
|
||||||
} else if (source.stage == ShaderType::Pixel) {
|
} else if (source.stage == ShaderType::Pixel) {
|
||||||
const auto* ps = pixel_input_info;
|
const auto* ps = pixel_input_info;
|
||||||
uint32_t reg = ps->ps_system_input_base;
|
uint32_t reg = ps->ps_system_input_base;
|
||||||
if (ps->ps_pos_x) {
|
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));
|
builtin(IR::StageInputKind::FragCoord, 0));
|
||||||
}
|
}
|
||||||
if (ps->ps_pos_y) {
|
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));
|
builtin(IR::StageInputKind::FragCoord, 1));
|
||||||
}
|
}
|
||||||
if (ps->ps_pos_z) {
|
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));
|
builtin(IR::StageInputKind::FragCoord, 2));
|
||||||
}
|
}
|
||||||
if (ps->ps_pos_w) {
|
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));
|
builtin(IR::StageInputKind::FragCoord, 3));
|
||||||
}
|
}
|
||||||
if (ps->ps_front_face) {
|
if (ps->ps_front_face) {
|
||||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(reg),
|
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(reg),
|
||||||
builtin(IR::StageInputKind::FrontFacing));
|
builtin(IR::StageInputKind::FrontFacing));
|
||||||
}
|
}
|
||||||
} else if (source.stage == ShaderType::Vertex) {
|
} 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));
|
builtin(IR::StageInputKind::VertexIndex));
|
||||||
prologue.SetVectorReg(static_cast<IR::VectorReg>(8),
|
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(8),
|
||||||
builtin(IR::StageInputKind::InstanceIndex));
|
builtin(IR::StageInputKind::InstanceIndex));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ namespace Libs::Graphics::ShaderRecompiler::IR {
|
|||||||
enum class ScalarReg : uint16_t {};
|
enum class ScalarReg : uint16_t {};
|
||||||
enum class VectorReg : uint16_t {};
|
enum class VectorReg : uint16_t {};
|
||||||
|
|
||||||
// Architectural SGPRs occupy the low range. The current shader-only migration also accepts the
|
constexpr uint32_t NumScalarRegs = 106;
|
||||||
// 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 NumVectorRegs = 256;
|
constexpr uint32_t NumVectorRegs = 256;
|
||||||
|
|
||||||
constexpr uint32_t RegIndex(ScalarReg reg) {
|
constexpr uint32_t RegIndex(ScalarReg reg) {
|
||||||
|
|||||||
@@ -12,20 +12,32 @@ using ShaderError::Fail;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool IsRegisterGet(ValueOpcode opcode) {
|
bool IsRegisterStatePseudo(ValueOpcode opcode) {
|
||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case ValueOpcode::GetThreadBitScalarRegister:
|
case ValueOpcode::GetThreadBitScalarRegister:
|
||||||
|
case ValueOpcode::SetThreadBitScalarRegister:
|
||||||
case ValueOpcode::GetScalarRegister:
|
case ValueOpcode::GetScalarRegister:
|
||||||
|
case ValueOpcode::SetScalarRegister:
|
||||||
case ValueOpcode::GetVectorRegister:
|
case ValueOpcode::GetVectorRegister:
|
||||||
|
case ValueOpcode::SetVectorRegister:
|
||||||
case ValueOpcode::GetGotoVariable:
|
case ValueOpcode::GetGotoVariable:
|
||||||
|
case ValueOpcode::SetGotoVariable:
|
||||||
case ValueOpcode::GetScc:
|
case ValueOpcode::GetScc:
|
||||||
|
case ValueOpcode::SetScc:
|
||||||
case ValueOpcode::GetExec:
|
case ValueOpcode::GetExec:
|
||||||
|
case ValueOpcode::SetExec:
|
||||||
case ValueOpcode::GetExecLo:
|
case ValueOpcode::GetExecLo:
|
||||||
|
case ValueOpcode::SetExecLo:
|
||||||
case ValueOpcode::GetExecHi:
|
case ValueOpcode::GetExecHi:
|
||||||
|
case ValueOpcode::SetExecHi:
|
||||||
case ValueOpcode::GetVcc:
|
case ValueOpcode::GetVcc:
|
||||||
|
case ValueOpcode::SetVcc:
|
||||||
case ValueOpcode::GetVccLo:
|
case ValueOpcode::GetVccLo:
|
||||||
|
case ValueOpcode::SetVccLo:
|
||||||
case ValueOpcode::GetVccHi:
|
case ValueOpcode::GetVccHi:
|
||||||
case ValueOpcode::GetM0: return true;
|
case ValueOpcode::SetVccHi:
|
||||||
|
case ValueOpcode::GetM0:
|
||||||
|
case ValueOpcode::SetM0: return true;
|
||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,8 +149,8 @@ bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::st
|
|||||||
if (inst.Parent() != block) {
|
if (inst.Parent() != block) {
|
||||||
return Fail(error, "value IR instruction has the wrong parent block");
|
return Fail(error, "value IR instruction has the wrong parent block");
|
||||||
}
|
}
|
||||||
if (require_ssa && IsRegisterGet(inst.GetOpcode())) {
|
if (require_ssa && IsRegisterStatePseudo(inst.GetOpcode())) {
|
||||||
return Fail(error, fmt::format("register getter {} survived SSA rewrite",
|
return Fail(error, fmt::format("register-state pseudo {} survived SSA rewrite",
|
||||||
ValueOpcodeName(inst.GetOpcode())));
|
ValueOpcodeName(inst.GetOpcode())));
|
||||||
}
|
}
|
||||||
if (inst.GetOpcode() != ValueOpcode::Phi && inst.GetOpcode() != ValueOpcode::Identity &&
|
if (inst.GetOpcode() != ValueOpcode::Phi && inst.GetOpcode() != ValueOpcode::Identity &&
|
||||||
|
|||||||
@@ -74,8 +74,6 @@ bool HasSideEffects(ValueOpcode opcode) {
|
|||||||
switch (opcode) {
|
switch (opcode) {
|
||||||
case ValueOpcode::Reference:
|
case ValueOpcode::Reference:
|
||||||
case ValueOpcode::ReferenceU32:
|
case ValueOpcode::ReferenceU32:
|
||||||
case ValueOpcode::Prologue:
|
|
||||||
case ValueOpcode::Epilogue:
|
|
||||||
case ValueOpcode::StoreAddressU8:
|
case ValueOpcode::StoreAddressU8:
|
||||||
case ValueOpcode::StoreAddressU16:
|
case ValueOpcode::StoreAddressU16:
|
||||||
case ValueOpcode::StoreAddressU32:
|
case ValueOpcode::StoreAddressU32:
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ VALUE_OPCODE(Reference, Void, U1)
|
|||||||
VALUE_OPCODE(ReferenceU32, Void, U32)
|
VALUE_OPCODE(ReferenceU32, Void, U32)
|
||||||
|
|
||||||
// Translation and register-state pseudo operations.
|
// Translation and register-state pseudo operations.
|
||||||
VALUE_OPCODE(Prologue, Void)
|
|
||||||
VALUE_OPCODE(Epilogue, Void)
|
|
||||||
VALUE_OPCODE(GetUserData, U32, ScalarReg)
|
VALUE_OPCODE(GetUserData, U32, ScalarReg)
|
||||||
VALUE_OPCODE(GetShaderBase, U64)
|
VALUE_OPCODE(GetShaderBase, U64)
|
||||||
VALUE_OPCODE(GetBuiltin, U32, U32, U32)
|
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/cfg/ShaderCFG.h"
|
||||||
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
|
#include "graphics/shader/recompiler/frontend/decode/ShaderDecoder.h"
|
||||||
#include "graphics/shader/recompiler/frontend/translate/Translate.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/ShaderIR.h"
|
||||||
#include "graphics/shader/recompiler/ir/ValueProgram.h"
|
#include "graphics/shader/recompiler/ir/ValueProgram.h"
|
||||||
#include "graphics/shader/recompiler/ir/passes/ConstantPropagation.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");
|
"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() {
|
void TestNewShaderRecompilerBufferLoadsGuardedByExec() {
|
||||||
const uint32_t shader[] = {
|
const uint32_t shader[] = {
|
||||||
EncodeMubuf0(0x0c),
|
EncodeMubuf0(0x0c),
|
||||||
@@ -7885,9 +7799,119 @@ void TestNewShaderRecompilerExpVertexOutputs() {
|
|||||||
CheckSpirvBinaryValidates(result.spirv);
|
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() {
|
void TestNewShaderRecompilerZeroInitialRegisterState() {
|
||||||
const uint32_t shader[] = {
|
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),
|
EncodeExp0(0x0c, 0xf),
|
||||||
EncodeExp1(0, 1, 2, 3), // POS0
|
EncodeExp1(0, 1, 2, 3), // POS0
|
||||||
EncodeSopp(0x01),
|
EncodeSopp(0x01),
|
||||||
@@ -7901,14 +7925,31 @@ void TestNewShaderRecompilerZeroInitialRegisterState() {
|
|||||||
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error),
|
Check(ShaderRecompiler::TryRecompile(shader, options, result, &error),
|
||||||
error.c_str());
|
error.c_str());
|
||||||
Check(!Common::ContainsStr(result.ir_dump, "UndefU32"),
|
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);
|
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||||
Check(!Common::ContainsStr(source, "OpUndef"),
|
Check(!Common::ContainsStr(source, "OpUndef"),
|
||||||
"zero-initialized guest registers became SPIR-V undef values");
|
"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);
|
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;
|
using StageInputKind = ShaderRecompiler::IR::StageInputKind;
|
||||||
|
|
||||||
const uint32_t shader[] = {
|
const uint32_t shader[] = {
|
||||||
@@ -7932,10 +7973,6 @@ void TestNewShaderRecompilerVertexSystemVgprs() {
|
|||||||
"vertex shader missing VertexIndex input");
|
"vertex shader missing VertexIndex input");
|
||||||
Check(ProgramHasInput(result.program, StageInputKind::InstanceIndex),
|
Check(ProgramHasInput(result.program, StageInputKind::InstanceIndex),
|
||||||
"vertex shader missing InstanceIndex input");
|
"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);
|
CheckSpirvBinaryValidates(result.spirv);
|
||||||
|
|
||||||
const auto source = DisassembleSpirvBinary(result.spirv);
|
const auto source = DisassembleSpirvBinary(result.spirv);
|
||||||
@@ -7943,10 +7980,9 @@ void TestNewShaderRecompilerVertexSystemVgprs() {
|
|||||||
"vertex SPIR-V does not load gl_VertexIndex");
|
"vertex SPIR-V does not load gl_VertexIndex");
|
||||||
Check(CountSourceOccurrences(source, "OpLoad %int %gl_InstanceIndex") == 1u,
|
Check(CountSourceOccurrences(source, "OpLoad %int %gl_InstanceIndex") == 1u,
|
||||||
"vertex SPIR-V does not load gl_InstanceIndex");
|
"vertex SPIR-V does not load gl_InstanceIndex");
|
||||||
Check(CountSourceOccurrences(source, "OpStore %v5") >= 2u,
|
Check(!Common::ContainsStr(source, "%v5") &&
|
||||||
"vertex SPIR-V does not seed guest v5 from gl_VertexIndex");
|
!Common::ContainsStr(source, "%v8"),
|
||||||
Check(CountSourceOccurrences(source, "OpStore %v8") >= 2u,
|
"vertex system values were routed through guest VGPR mirrors");
|
||||||
"vertex SPIR-V does not seed guest v8 from gl_InstanceIndex");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestNewShaderRecompilerVertexExportUsesLaneExecMask() {
|
void TestNewShaderRecompilerVertexExportUsesLaneExecMask() {
|
||||||
@@ -7988,9 +8024,9 @@ void TestNewShaderRecompilerVertexExportUsesLaneExecMask() {
|
|||||||
"per-invocation vertex export lost its EXEC guard");
|
"per-invocation vertex export lost its EXEC guard");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestNewShaderRecompilerPerInvocationMasks() {
|
void TestNewShaderRecompilerPerInvocationMasksWithoutMirrors() {
|
||||||
const uint32_t local_shader[] = {
|
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
|
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
|
EncodeSop1(0x24, 4, 126), // s_and_saveexec_b64 s[4:5], exec
|
||||||
EncodeSop2(0x25, 126, 132, 128), // s_bfm_b64 exec, 4, 0
|
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");
|
"per-invocation VCC producer still materialized a shared subgroup mask");
|
||||||
Check(!Common::ContainsStr(source, "OpLoad %uint %gl_SubgroupInvocationID"),
|
Check(!Common::ContainsStr(source, "OpLoad %uint %gl_SubgroupInvocationID"),
|
||||||
"per-invocation BFM EXEC prefix still selected native subgroup lanes");
|
"per-invocation BFM EXEC prefix still selected native subgroup lanes");
|
||||||
Check(Common::ContainsStr(source, "OpLogicalAnd") &&
|
Check(!Common::ContainsStr(source, "%vcc_lo") &&
|
||||||
Common::ContainsStr(source, "OpLogicalNot"),
|
!Common::ContainsStr(source, "%vcc_hi"),
|
||||||
"per-invocation wide mask ALU did not use Boolean operations");
|
"per-invocation comparison retained VCC register mirrors");
|
||||||
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");
|
|
||||||
|
|
||||||
const uint32_t wqm_shader[] = {
|
const uint32_t wqm_shader[] = {
|
||||||
EncodeSop1(0x0a, 2, 126), // s_wqm_b64 s[2:3], exec
|
EncodeSop1(0x0a, 2, 126), // s_wqm_b64 s[2:3], exec
|
||||||
|
EncodeVop1(0x01, 0, 2), // v_mov_b32 v0, s2
|
||||||
EncodeExp0(0x0c, 0xf),
|
EncodeExp0(0x0c, 0xf),
|
||||||
EncodeExp1(0, 1, 2, 3), // POS0
|
EncodeExp1(0, 1, 2, 3), // POS0
|
||||||
EncodeSopp(0x01),
|
EncodeSopp(0x01),
|
||||||
@@ -9760,8 +9792,12 @@ int main() {
|
|||||||
TestNewShaderRecompilerSetpcBranch();
|
TestNewShaderRecompilerSetpcBranch();
|
||||||
TestNewShaderRecompilerSetpcJumpTable();
|
TestNewShaderRecompilerSetpcJumpTable();
|
||||||
TestNewShaderRecompilerSetpcDwordJumpTable();
|
TestNewShaderRecompilerSetpcDwordJumpTable();
|
||||||
|
TestTypedEntryStateIsMinimal();
|
||||||
|
TestFinalSsaRejectsRegisterStatePseudos();
|
||||||
TestNewShaderRecompilerZeroInitialRegisterState();
|
TestNewShaderRecompilerZeroInitialRegisterState();
|
||||||
|
TestNewShaderRecompilerVertexSystemInputsWithoutMirrors();
|
||||||
TestNewShaderRecompilerVertexExportUsesLaneExecMask();
|
TestNewShaderRecompilerVertexExportUsesLaneExecMask();
|
||||||
|
TestNewShaderRecompilerPerInvocationMasksWithoutMirrors();
|
||||||
TestNewShaderRecompilerPerInvocationU64Complement();
|
TestNewShaderRecompilerPerInvocationU64Complement();
|
||||||
TestNewShaderRecompilerExpPixelOutputs();
|
TestNewShaderRecompilerExpPixelOutputs();
|
||||||
TestRenderTargetReverseExportMapping();
|
TestRenderTargetReverseExportMapping();
|
||||||
|
|||||||
Reference in New Issue
Block a user