mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 10:56:58 +02:00
shader: implement BUFFER_ATOMIC_FMAX
This commit is contained in:
@@ -85,6 +85,7 @@ bool InstructionMaySplitSpirvBlock(const IR::Instruction& inst) {
|
||||
case IR::Opcode::AtomicOrU32:
|
||||
case IR::Opcode::AtomicXorU32:
|
||||
case IR::Opcode::AtomicFMinF32:
|
||||
case IR::Opcode::AtomicFMaxF32:
|
||||
case IR::Opcode::FlatLoadUbyte:
|
||||
case IR::Opcode::FlatLoadSbyte:
|
||||
case IR::Opcode::FlatLoadUshort:
|
||||
|
||||
@@ -58,6 +58,7 @@ constexpr MemoryOpcodeInfo MUBUF_OPS[] = {
|
||||
{0x3au, Opcode::BufferAtomicOr, 1, 32},
|
||||
{0x3bu, Opcode::BufferAtomicXor, 1, 32},
|
||||
{0x3fu, Opcode::BufferAtomicFMin, 1, 32},
|
||||
{0x40u, Opcode::BufferAtomicFMax, 1, 32},
|
||||
};
|
||||
|
||||
constexpr MemoryOpcodeInfo MTBUF_OPS[] = {
|
||||
|
||||
@@ -836,6 +836,7 @@ std::string OpcodeToString(Opcode opcode) {
|
||||
case Opcode::BufferAtomicOr: return "buffer_atomic_or";
|
||||
case Opcode::BufferAtomicXor: return "buffer_atomic_xor";
|
||||
case Opcode::BufferAtomicFMin: return "buffer_atomic_fmin";
|
||||
case Opcode::BufferAtomicFMax: return "buffer_atomic_fmax";
|
||||
case Opcode::FlatLoadUbyte: return "flat_load_ubyte";
|
||||
case Opcode::FlatLoadSbyte: return "flat_load_sbyte";
|
||||
case Opcode::FlatLoadUshort: return "flat_load_ushort";
|
||||
@@ -1127,6 +1128,7 @@ std::string InstructionToString(const Instruction& inst) {
|
||||
case Opcode::BufferAtomicOr:
|
||||
case Opcode::BufferAtomicXor:
|
||||
case Opcode::BufferAtomicFMin:
|
||||
case Opcode::BufferAtomicFMax:
|
||||
case Opcode::BufferLoadSbyte:
|
||||
case Opcode::BufferLoadSshort:
|
||||
case Opcode::FlatLoadUbyte:
|
||||
|
||||
@@ -435,6 +435,7 @@ enum class Opcode {
|
||||
BufferAtomicOr,
|
||||
BufferAtomicXor,
|
||||
BufferAtomicFMin,
|
||||
BufferAtomicFMax,
|
||||
FlatLoadUbyte,
|
||||
FlatLoadSbyte,
|
||||
FlatLoadUshort,
|
||||
|
||||
@@ -132,7 +132,8 @@ bool IsAtomic(IR::Opcode op) {
|
||||
case IR::Opcode::AtomicAndU32:
|
||||
case IR::Opcode::AtomicOrU32:
|
||||
case IR::Opcode::AtomicXorU32:
|
||||
case IR::Opcode::AtomicFMinF32: return true;
|
||||
case IR::Opcode::AtomicFMinF32:
|
||||
case IR::Opcode::AtomicFMaxF32: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
@@ -142,6 +143,9 @@ uint32_t BufferAddressOperandCount(const IR::Instruction& inst) {
|
||||
}
|
||||
|
||||
bool ValidateInstructionContract(const IR::Instruction& inst, std::string* error) {
|
||||
if (static_cast<size_t>(inst.op) >= static_cast<size_t>(IR::Opcode::Count)) {
|
||||
return Fail(error, "IR instruction opcode is out of range");
|
||||
}
|
||||
if (inst.src_count > std::size(inst.src)) {
|
||||
return Fail(error, "instruction source count exceeds fixed IR storage");
|
||||
}
|
||||
@@ -201,6 +205,12 @@ bool ValidateInstructionContract(const IR::Instruction& inst, std::string* error
|
||||
inst.src_count != 2)) {
|
||||
return Fail(error, "storage image instruction has an invalid resource class");
|
||||
}
|
||||
const auto float_atomic =
|
||||
inst.op == IR::Opcode::AtomicFMinF32 || inst.op == IR::Opcode::AtomicFMaxF32;
|
||||
if (float_atomic &&
|
||||
(kind != IR::ResourceKind::Buffer || inst.src_count != buffer_address_count + 1u)) {
|
||||
return Fail(error, "floating-point atomic instruction must target a buffer");
|
||||
}
|
||||
if (IsAtomic(inst.op) &&
|
||||
!((kind == IR::ResourceKind::Buffer && inst.src_count == buffer_address_count + 1u) ||
|
||||
((kind == IR::ResourceKind::Lds || kind == IR::ResourceKind::Gds) &&
|
||||
@@ -489,6 +499,13 @@ bool EmitProgram(const IR::Program& program, const IR::ResourceSnapshot& resourc
|
||||
AllocateBlockLabels(state, program);
|
||||
AllocateDispatcherState(state, program);
|
||||
EmitFunction(state, program);
|
||||
if (state.unsupported_ir_instruction) {
|
||||
char pc[9] {};
|
||||
std::snprintf(pc, sizeof(pc), "%08x", state.unsupported_ir_pc);
|
||||
return Fail(error, "SPIR-V emitter has no handler for IR opcode " +
|
||||
std::string(IR::OpcodeName(state.unsupported_ir_opcode)) +
|
||||
" at pc 0x" + pc);
|
||||
}
|
||||
|
||||
ReportReserveExceeded(program, "registers", state.registers.size());
|
||||
ReportReserveExceeded(program, "inputs", state.inputs.size());
|
||||
|
||||
@@ -778,6 +778,9 @@ void EmitInstruction(EmitterState& state, const IR::Instruction& inst) {
|
||||
case IR::Opcode::AtomicFMinF32:
|
||||
EmitGuardedByExec(state, [&]() { EmitAtomicFMinF32(state, inst); });
|
||||
break;
|
||||
case IR::Opcode::AtomicFMaxF32:
|
||||
EmitGuardedByExec(state, [&]() { EmitAtomicFMaxF32(state, inst); });
|
||||
break;
|
||||
case IR::Opcode::FlatLoadUbyte: EmitFlatLoadUbyte(state, inst); break;
|
||||
case IR::Opcode::FlatLoadSbyte: EmitFlatLoadSbyte(state, inst); break;
|
||||
case IR::Opcode::FlatLoadUshort: EmitFlatLoadUshort(state, inst); break;
|
||||
@@ -854,7 +857,13 @@ void EmitInstruction(EmitterState& state, const IR::Instruction& inst) {
|
||||
}
|
||||
EmitGuardedByExec(state, [&]() { EmitExport(state, inst); });
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
if (!state.unsupported_ir_instruction) {
|
||||
state.unsupported_ir_instruction = true;
|
||||
state.unsupported_ir_opcode = inst.op;
|
||||
state.unsupported_ir_pc = inst.pc;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -414,6 +414,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;
|
||||
@@ -1012,6 +1015,7 @@ void EmitDeviceAtomicMemoryBarrier(EmitterState& state);
|
||||
void EmitAtomicU32(EmitterState& state, const IR::Instruction& inst, uint32_t opcode);
|
||||
|
||||
void EmitAtomicFMinF32(EmitterState& state, const IR::Instruction& inst);
|
||||
void EmitAtomicFMaxF32(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
void EmitSLoadDword(EmitterState& state, const IR::Instruction& inst);
|
||||
|
||||
|
||||
@@ -1184,7 +1184,8 @@ void EmitAtomicU32(EmitterState& state, const IR::Instruction& inst, uint32_t op
|
||||
|
||||
namespace {
|
||||
|
||||
uint32_t EmitF32BitsOrderedLessThan(EmitterState& state, uint32_t lhs_bits, uint32_t rhs_bits) {
|
||||
uint32_t EmitF32BitsOrderedCompare(EmitterState& state, uint32_t lhs_bits, uint32_t rhs_bits,
|
||||
uint32_t comparison_opcode) {
|
||||
struct ClassifiedBits {
|
||||
uint32_t nan = 0;
|
||||
uint32_t zero = 0;
|
||||
@@ -1219,14 +1220,13 @@ uint32_t EmitF32BitsOrderedLessThan(EmitterState& state, uint32_t lhs_bits, uint
|
||||
const auto both_zero = EmitLogicalAndBool(state, lhs.zero, rhs.zero);
|
||||
const auto ordered_nonzero =
|
||||
EmitLogicalNotBool(state, EmitLogicalOrBool(state, any_nan, both_zero));
|
||||
const auto less = state.builder.AllocateId();
|
||||
state.builder.AddFunction({OpULessThan, state.bool_type, less, lhs.key, rhs.key});
|
||||
return EmitLogicalAndBool(state, ordered_nonzero, less);
|
||||
const auto comparison = state.builder.AllocateId();
|
||||
state.builder.AddFunction({comparison_opcode, state.bool_type, comparison, lhs.key, rhs.key});
|
||||
return EmitLogicalAndBool(state, ordered_nonzero, comparison);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void EmitAtomicFMinF32(EmitterState& state, const IR::Instruction& inst) {
|
||||
void EmitAtomicFMinMaxF32(EmitterState& state, const IR::Instruction& inst,
|
||||
uint32_t comparison_opcode) {
|
||||
const auto index =
|
||||
EmitMemoryDwordIndex(state, inst, inst.memory, 1, AddressSourceCount(inst, 1));
|
||||
const auto in_bounds = EmitStorageBufferElementInBounds(state, inst.memory, index, inst.pc);
|
||||
@@ -1235,15 +1235,26 @@ void EmitAtomicFMinF32(EmitterState& state, const IR::Instruction& inst) {
|
||||
const auto pointer = EmitStorageBufferElementPointer(state, inst.memory, index, inst.pc);
|
||||
return EmitAtomicUpdateU32(state, pointer, inst.memory.kind, [&](uint32_t old_u32) {
|
||||
const auto replace_old = state.builder.AllocateId();
|
||||
const auto less = EmitF32BitsOrderedLessThan(state, src_u32, old_u32);
|
||||
const auto replace =
|
||||
EmitF32BitsOrderedCompare(state, src_u32, old_u32, comparison_opcode);
|
||||
state.builder.AddFunction(
|
||||
{OpSelect, state.uint_type, replace_old, less, src_u32, old_u32});
|
||||
{OpSelect, state.uint_type, replace_old, replace, src_u32, old_u32});
|
||||
return replace_old;
|
||||
});
|
||||
});
|
||||
EmitStoreU32(state, inst.dst, old);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void EmitAtomicFMinF32(EmitterState& state, const IR::Instruction& inst) {
|
||||
EmitAtomicFMinMaxF32(state, inst, OpULessThan);
|
||||
}
|
||||
|
||||
void EmitAtomicFMaxF32(EmitterState& state, const IR::Instruction& inst) {
|
||||
EmitAtomicFMinMaxF32(state, inst, OpUGreaterThan);
|
||||
}
|
||||
|
||||
void EmitSLoadDword(EmitterState& state, const IR::Instruction& inst) {
|
||||
if (state.address_memory_variable == 0) {
|
||||
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::AddressMemory,
|
||||
|
||||
@@ -31,7 +31,8 @@ bool IsAtomic(Opcode op) {
|
||||
case Opcode::AtomicAndU32:
|
||||
case Opcode::AtomicOrU32:
|
||||
case Opcode::AtomicXorU32:
|
||||
case Opcode::AtomicFMinF32: return true;
|
||||
case Opcode::AtomicFMinF32:
|
||||
case Opcode::AtomicFMaxF32: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -975,7 +975,12 @@ bool LowerControlInstruction(const Decoder::Instruction& decoded, BasicBlock& bl
|
||||
return LowerControlMarker(decoded, block, Opcode::TtraceData, true, error);
|
||||
case Decoder::Opcode::SInstPrefetch:
|
||||
return LowerControlMarker(decoded, block, Opcode::InstPrefetch, true, error);
|
||||
default: return false;
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("control opcode has no specialized IR lowering: {}",
|
||||
Decoder::OpcodeToString(decoded.opcode));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1064,6 +1069,18 @@ bool IsTerminatorOpcode(Decoder::Opcode opcode) {
|
||||
|
||||
bool LowerImplemented(const Decoder::Instruction& decoded, BasicBlock& block, std::string* error);
|
||||
|
||||
bool IsMemoryFamily(Decoder::Family family) {
|
||||
switch (family) {
|
||||
case Decoder::Family::SMEM:
|
||||
case Decoder::Family::MUBUF:
|
||||
case Decoder::Family::MTBUF:
|
||||
case Decoder::Family::FLAT:
|
||||
case Decoder::Family::DS:
|
||||
case Decoder::Family::MIMG: return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LowerDecodedInstruction(const Decoder::Instruction& inst, BasicBlock& block,
|
||||
std::string* error) {
|
||||
switch (inst.opcode) {
|
||||
@@ -1117,6 +1134,15 @@ bool LowerDecodedInstruction(const Decoder::Instruction& inst, BasicBlock& block
|
||||
if (IsMemoryOpcode(inst.opcode)) {
|
||||
return LowerMemoryInstruction(inst, block, error);
|
||||
}
|
||||
// Memory instructions require specialized lowering to populate MemoryInfo. Never let a
|
||||
// newly decoded memory-family opcode fall through to generic register-only IR.
|
||||
if (IsMemoryFamily(inst.family)) {
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("decoded memory-family opcode has no specialized IR lowering: {}",
|
||||
Decoder::OpcodeToString(inst.opcode));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (ScalarShiftLeftAddAmount(inst.opcode) != 0) {
|
||||
return LowerScalarShiftLeftAdd(inst, block, error);
|
||||
}
|
||||
|
||||
@@ -571,6 +571,8 @@ bool LowerMemoryInstruction(const Decoder::Instruction& decoded, BasicBlock& blo
|
||||
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicXorU32, error);
|
||||
case Decoder::Opcode::BufferAtomicFMin:
|
||||
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicFMinF32, error);
|
||||
case Decoder::Opcode::BufferAtomicFMax:
|
||||
return LowerBufferAtomicDword(decoded, block, Opcode::AtomicFMaxF32, error);
|
||||
case Decoder::Opcode::FlatLoadUbyte:
|
||||
case Decoder::Opcode::FlatLoadSbyte:
|
||||
case Decoder::Opcode::FlatLoadUshort:
|
||||
@@ -675,7 +677,12 @@ bool LowerMemoryInstruction(const Decoder::Instruction& decoded, BasicBlock& blo
|
||||
case Decoder::Opcode::ImageAtomicAnd:
|
||||
case Decoder::Opcode::ImageAtomicOr:
|
||||
case Decoder::Opcode::ImageAtomicXor: return LowerImageAtomicU32(decoded, block, error);
|
||||
default: return false;
|
||||
default:
|
||||
if (error != nullptr) {
|
||||
*error = fmt::format("memory opcode has no specialized IR lowering: {}",
|
||||
Decoder::OpcodeToString(decoded.opcode));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,6 +739,7 @@ bool IsMemoryOpcode(Decoder::Opcode opcode) {
|
||||
case Decoder::Opcode::BufferAtomicOr:
|
||||
case Decoder::Opcode::BufferAtomicXor:
|
||||
case Decoder::Opcode::BufferAtomicFMin:
|
||||
case Decoder::Opcode::BufferAtomicFMax:
|
||||
case Decoder::Opcode::FlatLoadUbyte:
|
||||
case Decoder::Opcode::FlatLoadSbyte:
|
||||
case Decoder::Opcode::FlatLoadUshort:
|
||||
|
||||
@@ -353,6 +353,7 @@ constexpr LowerMap LOWER_OPS[] = {
|
||||
{Decoder::Opcode::BufferAtomicOr, Opcode::AtomicOrU32},
|
||||
{Decoder::Opcode::BufferAtomicXor, Opcode::AtomicXorU32},
|
||||
{Decoder::Opcode::BufferAtomicFMin, Opcode::AtomicFMinF32},
|
||||
{Decoder::Opcode::BufferAtomicFMax, Opcode::AtomicFMaxF32},
|
||||
{Decoder::Opcode::FlatLoadUbyte, Opcode::FlatLoadUbyte},
|
||||
{Decoder::Opcode::FlatLoadSbyte, Opcode::FlatLoadSbyte},
|
||||
{Decoder::Opcode::FlatLoadSshort, Opcode::FlatLoadSshort},
|
||||
|
||||
@@ -308,6 +308,7 @@ IR_OPCODE(AtomicAndU32, General)
|
||||
IR_OPCODE(AtomicOrU32, General)
|
||||
IR_OPCODE(AtomicXorU32, General)
|
||||
IR_OPCODE(AtomicFMinF32, General)
|
||||
IR_OPCODE(AtomicFMaxF32, General)
|
||||
IR_OPCODE(FlatLoadUbyte, General)
|
||||
IR_OPCODE(FlatLoadSbyte, General)
|
||||
IR_OPCODE(FlatLoadUshort, General)
|
||||
|
||||
Reference in New Issue
Block a user