mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
refactor(shader): preserve native scalar memory operations
This commit is contained in:
@@ -11,15 +11,13 @@ bool Translator::TranslateU64MaskOperation(const IR::Instruction& inst) {
|
||||
: TranslateComposedInteger(inst);
|
||||
}
|
||||
|
||||
bool Translator::TranslateInstruction(const IR::Instruction& inst,
|
||||
const ScalarMemorySourceValues* scalar_source_snapshot) {
|
||||
bool Translator::TranslateInstruction(const IR::Instruction& inst) {
|
||||
switch (IR::GetOpcodeInfo(inst.op).lowering_class) {
|
||||
case IR::LoweringClass::Control: return TranslateControlOperation(inst);
|
||||
case IR::LoweringClass::Move: return TranslateMove(inst);
|
||||
case IR::LoweringClass::Lane: return TranslateLaneOperation(inst);
|
||||
case IR::LoweringClass::State: return TranslateStateOperation(inst);
|
||||
case IR::LoweringClass::Memory:
|
||||
return TranslateMemoryOperation(inst, scalar_source_snapshot);
|
||||
case IR::LoweringClass::Memory: return TranslateMemoryOperation(inst);
|
||||
case IR::LoweringClass::Attribute: return TranslateAttributeOperation(inst);
|
||||
case IR::LoweringClass::IntegerCompare: return TranslateIntegerCompare(inst);
|
||||
case IR::LoweringClass::Integer16Compare: return TranslateInteger16Compare(inst);
|
||||
|
||||
@@ -181,19 +181,7 @@ IR::ValueOpcode Translator::SharedAtomicOpcode(IR::Opcode opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Translator::IsScalarMemoryLoadOperation(IR::Opcode opcode) {
|
||||
return opcode == IR::Opcode::SLoadDword || opcode == IR::Opcode::SBufferLoadDword;
|
||||
}
|
||||
|
||||
ScalarMemorySourceValues Translator::ReadScalarMemorySource(const IR::Instruction& inst) {
|
||||
const auto resource = inst.op == IR::Opcode::SLoadDword
|
||||
? GetScalarAddressResource(inst.memory.resource)
|
||||
: GetBufferResource(inst.memory);
|
||||
return {resource, ReadU32(inst.src[0])};
|
||||
}
|
||||
|
||||
bool Translator::TranslateScalarMemory(const IR::Instruction& inst,
|
||||
const ScalarMemorySourceValues* source_snapshot) {
|
||||
bool Translator::TranslateScalarMemory(const IR::Instruction& inst) {
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::LoadSrtDword: {
|
||||
const auto resource = ir.Emit(IR::ValueOpcode::GetSrtResource);
|
||||
@@ -201,20 +189,30 @@ bool Translator::TranslateScalarMemory(const IR::Instruction& inst,
|
||||
ir.Emit(IR::ValueOpcode::ReadConst, {resource, ReadU32(inst.src[0])}));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::SBufferLoadDword: {
|
||||
const auto source =
|
||||
source_snapshot != nullptr ? *source_snapshot : ReadScalarMemorySource(inst);
|
||||
WriteOperand(inst.dst, ir.Emit(IR::ValueOpcode::ReadConstBuffer,
|
||||
{source.resource, source.offset}, AddMemoryInfo(inst)));
|
||||
return true;
|
||||
}
|
||||
case IR::Opcode::SBufferLoadDword:
|
||||
case IR::Opcode::SLoadDword: {
|
||||
const auto source =
|
||||
source_snapshot != nullptr ? *source_snapshot : ReadScalarMemorySource(inst);
|
||||
WriteOperand(inst.dst,
|
||||
ir.Emit(IR::ValueOpcode::LoadAddressU32,
|
||||
{source.resource, source.offset, IR::Value(0u), IR::Value(true)},
|
||||
AddMemoryInfo(inst)));
|
||||
const auto resource = inst.op == IR::Opcode::SLoadDword
|
||||
? GetScalarAddressResource(inst.memory.resource)
|
||||
: GetBufferResource(inst.memory);
|
||||
const auto offset = ReadU32(inst.src[0]);
|
||||
std::array<IR::Value, 16> loaded {};
|
||||
for (uint32_t component = 0; component < inst.memory.data_dwords; component++) {
|
||||
auto scalar = inst;
|
||||
scalar.memory.offset += component * sizeof(uint32_t);
|
||||
scalar.memory.data_dwords = 1u;
|
||||
scalar.memory.component_index = component;
|
||||
if (inst.op == IR::Opcode::SBufferLoadDword) {
|
||||
loaded[component] = ir.Emit(IR::ValueOpcode::ReadConstBuffer,
|
||||
{resource, offset}, AddMemoryInfo(scalar));
|
||||
} else {
|
||||
loaded[component] = ir.Emit(
|
||||
IR::ValueOpcode::LoadAddressU32,
|
||||
{resource, offset, IR::Value(0u), IR::Value(true)}, AddMemoryInfo(scalar));
|
||||
}
|
||||
}
|
||||
for (uint32_t component = 0; component < inst.memory.data_dwords; component++) {
|
||||
WriteOperand(ScalarDestinationOperand(inst.dst, component), loaded[component]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default: return false;
|
||||
@@ -681,13 +679,12 @@ bool Translator::TranslateSharedMemory(const IR::Instruction& inst) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Translator::TranslateMemoryOperation(const IR::Instruction& inst,
|
||||
const ScalarMemorySourceValues* scalar_source_snapshot) {
|
||||
bool Translator::TranslateMemoryOperation(const IR::Instruction& inst) {
|
||||
switch (inst.op) {
|
||||
case IR::Opcode::LoadSrtDword:
|
||||
case IR::Opcode::SLoadDword:
|
||||
case IR::Opcode::SBufferLoadDword:
|
||||
return TranslateScalarMemory(inst, scalar_source_snapshot);
|
||||
return TranslateScalarMemory(inst);
|
||||
|
||||
case IR::Opcode::BufferLoadUbyte:
|
||||
case IR::Opcode::BufferLoadSbyte:
|
||||
|
||||
@@ -37,6 +37,29 @@ IR::Operand Translator::OffsetOperand(const IR::Operand& operand, uint32_t offse
|
||||
return result;
|
||||
}
|
||||
|
||||
IR::Operand Translator::ScalarDestinationOperand(const IR::Operand& operand, uint32_t offset) {
|
||||
EXIT_IF(operand.kind != IR::OperandKind::Register);
|
||||
uint32_t code = 0;
|
||||
switch (operand.reg.file) {
|
||||
case IR::RegisterFile::Scalar: code = operand.reg.index; break;
|
||||
case IR::RegisterFile::Vcc: code = 106u + operand.reg.index; break;
|
||||
default: EXIT("invalid scalar-memory destination");
|
||||
}
|
||||
code += offset;
|
||||
IR::Operand result;
|
||||
result.kind = IR::OperandKind::Register;
|
||||
if (code < 106u) {
|
||||
result.reg = {IR::RegisterFile::Scalar, code};
|
||||
} else {
|
||||
switch (code) {
|
||||
case 106u: result.reg = {IR::RegisterFile::Vcc, 0u}; break;
|
||||
case 107u: result.reg = {IR::RegisterFile::Vcc, 1u}; break;
|
||||
default: EXIT("scalar-memory destination crosses an invalid register");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
IR::Operand Translator::PlainOperand(const IR::Operand& operand) {
|
||||
auto result = operand;
|
||||
result.sdwa_sel = 6;
|
||||
@@ -653,28 +676,13 @@ void Translator::WriteCompareResult(const IR::Operand& operand, IR::U1 value) {
|
||||
}
|
||||
|
||||
bool Translator::TranslateBlock(const IR::BasicBlock& source, std::string* error) {
|
||||
ScalarMemorySourceValues scalar_source_snapshot {};
|
||||
uint32_t scalar_source_pc = 0;
|
||||
bool has_scalar_source_snapshot = false;
|
||||
for (const auto& source_inst: source.instructions) {
|
||||
current_opcode = source_inst.op;
|
||||
current_pc = source_inst.pc;
|
||||
const bool grouped_scalar_load =
|
||||
IsScalarMemoryLoadOperation(source_inst.op) && source_inst.memory.component_count > 1u;
|
||||
if (grouped_scalar_load &&
|
||||
(!has_scalar_source_snapshot || scalar_source_pc != source_inst.pc ||
|
||||
source_inst.memory.component_index == 0u)) {
|
||||
scalar_source_snapshot = ReadScalarMemorySource(source_inst);
|
||||
scalar_source_pc = source_inst.pc;
|
||||
has_scalar_source_snapshot = true;
|
||||
} else if (!grouped_scalar_load) {
|
||||
has_scalar_source_snapshot = false;
|
||||
}
|
||||
if (static_cast<size_t>(source_inst.op) >= static_cast<size_t>(IR::Opcode::Count)) {
|
||||
return Fail(error, "value IR input opcode is out of range");
|
||||
}
|
||||
if (TranslateInstruction(source_inst,
|
||||
grouped_scalar_load ? &scalar_source_snapshot : nullptr)) {
|
||||
if (TranslateInstruction(source_inst)) {
|
||||
continue;
|
||||
}
|
||||
return Fail(error, fmt::format("opcode {} at 0x{:08x} has no typed Value IR lowering",
|
||||
|
||||
@@ -7,11 +7,6 @@
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::Frontend::Detail {
|
||||
|
||||
struct ScalarMemorySourceValues {
|
||||
IR::Value resource;
|
||||
IR::U32 offset;
|
||||
};
|
||||
|
||||
class Translator {
|
||||
public:
|
||||
Translator(IR::ValueProgram& program, IR::Block* block, uint32_t vector_limit,
|
||||
@@ -25,6 +20,7 @@ public:
|
||||
|
||||
private:
|
||||
IR::Operand OffsetOperand(const IR::Operand& operand, uint32_t offset);
|
||||
IR::Operand ScalarDestinationOperand(const IR::Operand& operand, uint32_t offset);
|
||||
IR::Operand PlainOperand(const IR::Operand& operand);
|
||||
IR::U32 ReadPcRelativeU32(const IR::Operand& operand);
|
||||
std::array<IR::U32, 2> BallotMask(IR::U1 value);
|
||||
@@ -85,10 +81,7 @@ private:
|
||||
IR::Value NarrowSubdword(IR::U32 value, uint32_t bits);
|
||||
IR::ValueOpcode BufferAtomicOpcode(IR::Opcode opcode);
|
||||
IR::ValueOpcode SharedAtomicOpcode(IR::Opcode opcode);
|
||||
bool IsScalarMemoryLoadOperation(IR::Opcode opcode);
|
||||
ScalarMemorySourceValues ReadScalarMemorySource(const IR::Instruction& inst);
|
||||
bool TranslateScalarMemory(const IR::Instruction& inst,
|
||||
const ScalarMemorySourceValues* source_snapshot);
|
||||
bool TranslateScalarMemory(const IR::Instruction& inst);
|
||||
bool TranslateBufferLoad(const IR::Instruction& inst);
|
||||
bool TranslateBufferStore(const IR::Instruction& inst);
|
||||
bool TranslateAtomicMemory(const IR::Instruction& inst);
|
||||
@@ -105,15 +98,13 @@ private:
|
||||
uint32_t upper_result);
|
||||
IR::U32 PackU16Lanes(IR::U32 low, IR::U32 high);
|
||||
|
||||
bool TranslateInstruction(const IR::Instruction& inst,
|
||||
const ScalarMemorySourceValues* scalar_source_snapshot);
|
||||
bool TranslateInstruction(const IR::Instruction& inst);
|
||||
bool TranslateStateOperation(const IR::Instruction& inst);
|
||||
bool TranslateControlOperation(const IR::Instruction& inst);
|
||||
bool TranslateMove(const IR::Instruction& inst);
|
||||
bool TranslateLaneOperation(const IR::Instruction& inst);
|
||||
bool TranslateAttributeOperation(const IR::Instruction& inst);
|
||||
bool TranslateMemoryOperation(const IR::Instruction& inst,
|
||||
const ScalarMemorySourceValues* scalar_source_snapshot);
|
||||
bool TranslateMemoryOperation(const IR::Instruction& inst);
|
||||
bool TranslateIntegerCompare(const IR::Instruction& inst);
|
||||
bool TranslateInteger16Compare(const IR::Instruction& inst);
|
||||
bool TranslateFloatCompare(const IR::Instruction& inst);
|
||||
|
||||
@@ -273,7 +273,7 @@ void ClearRegisterOffsetModifiers(Decoder::Operand& operand) {
|
||||
operand.dpp = false;
|
||||
}
|
||||
|
||||
bool TryGetScalarDestinationCode(const Decoder::Operand& operand, uint32_t& code) {
|
||||
bool TryGetEncodedScalarCode(const Decoder::Operand& operand, uint32_t& code) {
|
||||
switch (operand.kind) {
|
||||
case Decoder::OperandKind::Sgpr: code = operand.reg; return true;
|
||||
case Decoder::OperandKind::VccLo: code = SCALAR_DST_VCC_LO; return true;
|
||||
@@ -290,7 +290,7 @@ bool TryOffsetScalarDestination(const Decoder::Operand& operand, uint32_t index,
|
||||
// aliases such as VCC and EXEC sit beside ordinary SGPRs. Re-decode after adding the
|
||||
// dword index so s_buffer_load_dwordx2 vcc_lo writes vcc_lo, then vcc_hi.
|
||||
uint32_t code = 0;
|
||||
return TryGetScalarDestinationCode(operand, code) &&
|
||||
return TryGetEncodedScalarCode(operand, code) &&
|
||||
Decoder::DecodeScalarDestination(code + index, 0, decoded, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ uint32_t ResourceIndexFromOperand(const Decoder::Operand& operand);
|
||||
MemoryInfo MemoryInfoFromDecoded(const Decoder::Instruction& decoded, ResourceKind kind);
|
||||
uint32_t RawScalarLoadBase(const Decoder::Operand& operand);
|
||||
void ClearRegisterOffsetModifiers(Decoder::Operand& operand);
|
||||
bool TryGetScalarDestinationCode(const Decoder::Operand& operand, uint32_t& code);
|
||||
bool TryGetEncodedScalarCode(const Decoder::Operand& operand, uint32_t& code);
|
||||
bool TryOffsetScalarDestination(const Decoder::Operand& operand, uint32_t index,
|
||||
Decoder::Operand& result);
|
||||
Decoder::Operand OffsetDecodedRegister(const Decoder::Operand& operand, uint32_t index);
|
||||
|
||||
@@ -77,24 +77,40 @@ Opcode LoweredImageOpcode(Decoder::Opcode opcode) {
|
||||
|
||||
bool LowerScalarMemoryLoadDword(const Decoder::Instruction& decoded, BasicBlock& block, Opcode op,
|
||||
std::string* error) {
|
||||
for (uint32_t i = 0; i < decoded.data_dwords; i++) {
|
||||
Instruction inst;
|
||||
inst.pc = decoded.pc;
|
||||
inst.op = op;
|
||||
inst.src_count = 1;
|
||||
const auto kind =
|
||||
op == Opcode::SLoadDword ? ResourceKind::ScalarAddress : ResourceKind::ScalarBuffer;
|
||||
inst.memory = OffsetMemoryInfo(decoded, kind, i);
|
||||
// Raw s_load uses a two-SGPR pointer base; s_buffer_load uses a four-SGPR
|
||||
// descriptor resource index.
|
||||
inst.memory.resource = op == Opcode::SLoadDword ? RawScalarLoadBase(decoded.src0)
|
||||
: ResourceIndexFromOperand(decoded.src0);
|
||||
if (!LowerRegisterOperand(OffsetDecodedRegister(decoded.dst, i), inst.dst, error) ||
|
||||
!LowerSourceOperand(decoded.src1, inst.src[0], error)) {
|
||||
return false;
|
||||
}
|
||||
block.instructions.push_back(inst);
|
||||
uint32_t destination_code = 0;
|
||||
const auto alignment = decoded.data_dwords == 1u ? 1u
|
||||
: decoded.data_dwords == 2u ? 2u
|
||||
: 4u;
|
||||
if (!TryGetEncodedScalarCode(decoded.dst, destination_code) ||
|
||||
destination_code % alignment != 0u ||
|
||||
destination_code + decoded.data_dwords > 108u) {
|
||||
SetError(error, "scalar-memory destination has an invalid aligned SGPR span");
|
||||
return false;
|
||||
}
|
||||
uint32_t base_code = 0;
|
||||
const auto base_alignment = op == Opcode::SLoadDword ? 2u : 4u;
|
||||
const auto base_width = op == Opcode::SLoadDword ? 2u : 4u;
|
||||
if (!TryGetEncodedScalarCode(decoded.src0, base_code) ||
|
||||
base_code % base_alignment != 0u || base_code + base_width > 108u) {
|
||||
SetError(error, "scalar-memory base has an invalid aligned SGPR span");
|
||||
return false;
|
||||
}
|
||||
Instruction inst;
|
||||
inst.pc = decoded.pc;
|
||||
inst.op = op;
|
||||
inst.src_count = 1;
|
||||
const auto kind =
|
||||
op == Opcode::SLoadDword ? ResourceKind::ScalarAddress : ResourceKind::ScalarBuffer;
|
||||
inst.memory = MemoryInfoFromDecoded(decoded, kind);
|
||||
// Raw s_load uses a two-SGPR pointer base; s_buffer_load uses a four-SGPR
|
||||
// descriptor resource index.
|
||||
inst.memory.resource = op == Opcode::SLoadDword ? RawScalarLoadBase(decoded.src0)
|
||||
: ResourceIndexFromOperand(decoded.src0);
|
||||
if (!LowerRegisterOperand(decoded.dst, inst.dst, error) ||
|
||||
!LowerSourceOperand(decoded.src1, inst.src[0], error)) {
|
||||
return false;
|
||||
}
|
||||
block.instructions.push_back(inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -311,6 +311,33 @@ bool ValidateValueProgram(const ValueProgram& program, bool require_ssa, std::st
|
||||
ValueOpcodeName(inst.GetOpcode()), inst.NumArgs(),
|
||||
NumArgsOf(inst.GetOpcode())));
|
||||
}
|
||||
if (IsRuntimeRead(inst.GetOpcode())) {
|
||||
const auto memory_index = inst.Flags<MemoryFlags>().index;
|
||||
if (memory_index >= program.memory_info.size()) {
|
||||
return Fail(error, fmt::format("{} has an invalid memory-info index",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
const auto& memory = program.memory_info[memory_index];
|
||||
const bool scalar_buffer = inst.GetOpcode() == ValueOpcode::ReadConstBuffer;
|
||||
const bool scalar_address = memory.kind == ResourceKind::ScalarAddress;
|
||||
if ((scalar_buffer && memory.kind != ResourceKind::ScalarBuffer) ||
|
||||
(!scalar_buffer && !scalar_address && memory.kind != ResourceKind::Flat &&
|
||||
memory.kind != ResourceKind::Global && memory.kind != ResourceKind::Scratch)) {
|
||||
return Fail(error, fmt::format("{} has an invalid scalar-memory resource kind",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
const bool scalar_memory = scalar_buffer || scalar_address;
|
||||
const bool valid_group_width =
|
||||
scalar_memory ? memory.component_count == 1u || memory.component_count == 2u ||
|
||||
memory.component_count == 4u || memory.component_count == 8u ||
|
||||
memory.component_count == 16u
|
||||
: memory.component_count >= 1u && memory.component_count <= 4u;
|
||||
if (memory.data_bits != 32u || memory.data_dwords != 1u ||
|
||||
!valid_group_width || memory.component_index >= memory.component_count) {
|
||||
return Fail(error, fmt::format("{} has inconsistent scalar-memory metadata",
|
||||
ValueOpcodeName(inst.GetOpcode())));
|
||||
}
|
||||
}
|
||||
const auto buffer_components = BufferComponentCount(inst.GetOpcode());
|
||||
if (buffer_components != 0u) {
|
||||
const auto memory_index = inst.Flags<MemoryFlags>().index;
|
||||
|
||||
Reference in New Issue
Block a user