graphics: separate scalar address bindings

This commit is contained in:
nmzik
2026-08-16 08:04:07 +02:00
parent 83ee040b4d
commit 443bbcbf90
12 changed files with 229 additions and 32 deletions
@@ -302,9 +302,9 @@ uint32_t EmitBufferByteAddress(EmitterState& state, const IR::Instruction& inst,
return EmitBufferAddressFromParts(state, inst, index, offset, soffset);
}
bool IsFlatMemoryKind(IR::ResourceKind kind) {
return kind == IR::ResourceKind::Flat || kind == IR::ResourceKind::Global ||
kind == IR::ResourceKind::Scratch;
bool IsAddressMemoryKind(IR::ResourceKind kind) {
return kind == IR::ResourceKind::ScalarAddress || kind == IR::ResourceKind::Flat ||
kind == IR::ResourceKind::Global || kind == IR::ResourceKind::Scratch;
}
uint32_t EmitRelativeAddress(EmitterState& state, const IR::Instruction& inst, uint32_t first_src,
@@ -423,7 +423,7 @@ StorageBufferBindingForMemory(EmitterState& state, const IR::MemoryInfo& mem, ui
uint32_t EmitStorageBufferIndex(EmitterState& state, const IR::MemoryInfo& mem, uint32_t index,
uint32_t use_pc) {
if (IsFlatMemoryKind(mem.kind)) {
if (IsAddressMemoryKind(mem.kind)) {
return index;
}
const auto binding = StorageBufferBindingForMemory(state, mem, use_pc);
@@ -432,7 +432,7 @@ uint32_t EmitStorageBufferIndex(EmitterState& state, const IR::MemoryInfo& mem,
uint32_t EmitStorageBufferObjectPointer(EmitterState& state, const IR::MemoryInfo& mem,
uint32_t use_pc) {
if (IsFlatMemoryKind(mem.kind)) {
if (IsAddressMemoryKind(mem.kind)) {
if (state.address_memory_variable == 0) {
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::AddressMemory,
mem.resource, "address memory binding was not emitted");
@@ -467,7 +467,7 @@ uint32_t EmitStorageBufferElementInBounds(EmitterState& state, const IR::MemoryI
uint32_t EmitStorageBufferElementPointer(EmitterState& state, const IR::MemoryInfo& mem,
uint32_t index, uint32_t use_pc) {
index = EmitStorageBufferIndex(state, mem, index, use_pc);
if (IsFlatMemoryKind(mem.kind)) {
if (IsAddressMemoryKind(mem.kind)) {
if (state.address_memory_variable == 0) {
ExitDescriptorBindingFailure(state, IR::DescriptorBindingKind::AddressMemory,
mem.resource, "address memory binding was not emitted");
@@ -102,8 +102,12 @@ uint32_t AddU64Low(EmitterState& state, uint32_t low, uint32_t high, uint32_t ad
uint32_t AddressByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem,
uint32_t low, uint32_t high) {
auto& state = ctx.state;
const auto immediate = static_cast<int32_t>(mem.offset);
auto& state = ctx.state;
auto immediate = static_cast<int32_t>(mem.offset);
if (mem.kind == IR::ResourceKind::ScalarAddress) {
immediate = static_cast<int32_t>(static_cast<uint32_t>(immediate) & ~3u);
low = Binary(state, OpBitwiseAnd, state.uint_type, low, ConstantU32(state, ~3u));
}
const auto immediate_low = ConstantU32(state, static_cast<uint32_t>(immediate));
const auto immediate_high = ConstantU32(state, immediate < 0 ? UINT32_MAX : 0u);
const auto base = state.program.info.addresses[mem.resource].specialized_base;
@@ -137,11 +141,7 @@ uint32_t AddressByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const I
}
uint32_t ByteAddress(ValueEmitContext& ctx, const IR::Inst& inst, const IR::MemoryInfo& mem) {
if (mem.kind == IR::ResourceKind::Buffer || mem.kind == IR::ResourceKind::ScalarBuffer) {
if (mem.kind == IR::ResourceKind::ScalarBuffer) {
return Binary(ctx.state, OpIAdd, ctx.state.uint_type, ctx.Arg(inst, 1),
ConstantU32(ctx.state, mem.offset));
}
if (mem.kind == IR::ResourceKind::Buffer) {
return BufferByteAddress(ctx, inst, mem, ctx.Arg(inst, 1), ctx.Arg(inst, 2),
ctx.Arg(inst, 3));
}
@@ -59,6 +59,7 @@ struct Operand {
enum class ResourceKind {
None,
ScalarBuffer,
ScalarAddress,
Buffer,
Flat,
Global,
@@ -74,6 +74,7 @@ std::string OperandToString(const Operand& operand) {
std::string ResourceKindToString(ResourceKind kind) {
switch (kind) {
case ResourceKind::ScalarBuffer: return "scalar_buffer";
case ResourceKind::ScalarAddress: return "scalar_address";
case ResourceKind::Buffer: return "buffer";
case ResourceKind::Flat: return "flat";
case ResourceKind::Global: return "global";
@@ -75,14 +75,16 @@ Opcode LoweredImageOpcode(Decoder::Opcode opcode) {
}
}
bool LowerScalarBufferLoadDword(const Decoder::Instruction& decoded, BasicBlock& block, Opcode op,
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;
inst.memory = OffsetMemoryInfo(decoded, ResourceKind::ScalarBuffer, i);
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)
@@ -511,13 +513,13 @@ bool LowerMemoryInstruction(const Decoder::Instruction& decoded, BasicBlock& blo
case Decoder::Opcode::SLoadDwordx4:
case Decoder::Opcode::SLoadDwordx8:
case Decoder::Opcode::SLoadDwordx16:
return LowerScalarBufferLoadDword(decoded, block, Opcode::SLoadDword, error);
return LowerScalarMemoryLoadDword(decoded, block, Opcode::SLoadDword, error);
case Decoder::Opcode::SBufferLoadDword:
case Decoder::Opcode::SBufferLoadDwordx2:
case Decoder::Opcode::SBufferLoadDwordx4:
case Decoder::Opcode::SBufferLoadDwordx8:
case Decoder::Opcode::SBufferLoadDwordx16:
return LowerScalarBufferLoadDword(decoded, block, Opcode::SBufferLoadDword, error);
return LowerScalarMemoryLoadDword(decoded, block, Opcode::SBufferLoadDword, error);
case Decoder::Opcode::BufferLoadUbyte:
case Decoder::Opcode::BufferLoadSbyte:
case Decoder::Opcode::BufferLoadUshort:
@@ -559,7 +559,7 @@ bool MaterializeResources(const Program& program, const SrtRuntime& runtime,
auto base = (static_cast<uint64_t>(value.dwords[0]) |
static_cast<uint64_t>(value.dwords[1]) << 32u) &
AddressMask;
if (address.kind == ResourceKind::ScalarBuffer) {
if (address.kind == ResourceKind::ScalarAddress) {
base &= ~uint64_t {3};
}
const auto before = static_cast<uint64_t>(-static_cast<int64_t>(address.min_offset));
@@ -724,7 +724,7 @@ private:
uint32_t AddAddress(uint32_t source, bool unbased, const MemoryInfo& memory, ValueOpcode op,
uint32_t pc) {
auto immediate = static_cast<int32_t>(memory.offset);
if (memory.kind == ResourceKind::ScalarBuffer) {
if (memory.kind == ResourceKind::ScalarAddress) {
immediate = static_cast<int32_t>(static_cast<uint32_t>(immediate) & ~3u);
}
const auto min_offset = unbased ? 0 : std::min(immediate, 0);
@@ -50,13 +50,17 @@ bool AddSignedAddress(uint64_t base, int64_t offset, uint64_t& result) {
}
bool IsRawRead(const ValueProgram& values, const Inst& inst) {
if (inst.GetOpcode() != ValueOpcode::LoadAddressU32 &&
inst.GetOpcode() != ValueOpcode::ReadConstBuffer) {
const auto op = inst.GetOpcode();
if (op != ValueOpcode::LoadAddressU32 && op != ValueOpcode::ReadConstBuffer) {
return false;
}
const auto index = inst.Flags<MemoryFlags>().index;
return index < values.memory_info.size() &&
values.memory_info[index].kind == ResourceKind::ScalarBuffer;
if (index >= values.memory_info.size()) {
return false;
}
const auto kind = values.memory_info[index].kind;
return (op == ValueOpcode::LoadAddressU32 && kind == ResourceKind::ScalarAddress) ||
(op == ValueOpcode::ReadConstBuffer && kind == ResourceKind::ScalarBuffer);
}
bool IsDescriptorHandle(ValueOpcode opcode) {
@@ -262,6 +266,22 @@ public:
m_values.dynamic_reads.clear();
for (auto* block: m_values.blocks) {
for (auto& inst: *block) {
const auto op = inst.GetOpcode();
if (op == ValueOpcode::LoadAddressU32 || op == ValueOpcode::ReadConstBuffer) {
const auto flags = inst.Flags<MemoryFlags>();
if (flags.index < m_values.memory_info.size()) {
const auto kind = m_values.memory_info[flags.index].kind;
const bool crosswired = (op == ValueOpcode::LoadAddressU32 &&
kind == ResourceKind::ScalarBuffer) ||
(op == ValueOpcode::ReadConstBuffer &&
kind == ResourceKind::ScalarAddress);
if (crosswired) {
return Fail(flags.pc, error,
fmt::format("{} has incompatible scalar memory metadata",
ValueOpcodeName(op)));
}
}
}
if (IsDescriptorHandle(inst.GetOpcode())) {
for (size_t index = 0; index < inst.NumArgs(); index++) {
if (!Collect(inst.Arg(index), 0, error)) {