shader: load bytes and halfwords from GPU-selected buffers

Co-authored-by: fxpw <fxpw@outlook.com>
This commit is contained in:
nmzik
2026-10-14 04:35:21 +02:00
parent 8d18be71db
commit 7bba785963
5 changed files with 98 additions and 18 deletions
@@ -970,6 +970,7 @@ uint32_t IndirectBufferInBounds(EmitterState& state, const IndirectBufferAccess&
uint32_t LoadIndirectBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_t components) {
auto& state = ctx.state;
const auto bits = ctx.Memory(inst).data_bits;
const auto buffer = PrepareIndirectBuffer(ctx, inst);
const auto valid_format = Binary(state, spv::OpINotEqual, TypeBool(state), buffer.format,
ConstantU32(state, 0));
@@ -983,8 +984,19 @@ uint32_t LoadIndirectBuffer(ValueEmitContext& ctx, const IR::Inst& inst, uint32_
values[component] = EmitValueOrZeroIfCondition(
state, AndCondition(state, valid_format,
IndirectBufferInBounds(state, buffer, component * 4u,
ConstantU32(state, 4u), false)),
[&]() { return LoadBdaDword(ctx, address); });
ConstantU32(state, bits / 8u), false)),
[&]() {
const auto value = LoadBdaDword(ctx, address, ctx.Memory(inst).coherent);
if (bits == 32u) return value;
const auto byte = Binary(
state, spv::OpBitwiseAnd, TypeU32(state),
Unary(state, spv::OpUConvert, TypeU32(state), buffer.address),
ConstantU32(state, bits == 8u ? 3u : 2u));
return EmitBitFieldUExtract(
state, value,
Binary(state, spv::OpShiftLeftLogical, TypeU32(state), byte, ConstantU32(state, 3u)),
ConstantU32(state, bits));
});
}
return ConstructU32Composite(state, components, values);
}
@@ -1444,8 +1456,8 @@ void EmitLoadMemory(ValueEmitContext& ctx, const IR::Inst& inst) {
if (mem.kind == IR::ResourceKind::FlatLocal)
value = LoadLocalFlat(ctx, inst);
else if (buffer_components > 1u ||
(op == IR::ValueOpcode::LoadBufferU32 &&
(mem.kind == IR::ResourceKind::IndirectBuffer || mem.formatted)))
(buffer_components == 1u && mem.kind == IR::ResourceKind::IndirectBuffer) ||
(op == IR::ValueOpcode::LoadBufferU32 && mem.formatted))
value = LoadBuffer(ctx, inst, buffer_components);
else if (shared_components > 1u)
value = LoadWideShared(ctx, inst, shared_components);
@@ -471,7 +471,8 @@ void ValidateProgram(const Program& program, bool require_ssa) {
}
if (memory.kind == ResourceKind::IndirectBuffer &&
!memory.SupportsIndirectBufferLoad(inst.GetOpcode())) {
return Fail("indirect buffer requires a scalar, raw DWORD x1/x2/x3/x4, or formatted X load");
return Fail("indirect buffer requires a scalar, raw 8/16-bit, raw DWORD x1/x2/x3/x4, "
"or formatted X load");
}
const bool packed_formatted_load =
BufferAccessOf(inst.GetOpcode()) == BufferAccess::Read && memory.formatted &&
+4 -1
View File
@@ -83,7 +83,10 @@ struct MemoryInfo {
bool planning_only = false;
[[nodiscard]] bool SupportsIndirectBufferLoad(ValueOpcode opcode) const {
return !typed && data_bits == 32u &&
if (typed) return false;
if (data_bits == 8u) return !formatted && opcode == ValueOpcode::LoadBufferU8;
if (data_bits == 16u) return !formatted && opcode == ValueOpcode::LoadBufferU16;
return data_bits == 32u &&
(formatted ? opcode == ValueOpcode::LoadBufferU32
: opcode == ValueOpcode::ReadConstBuffer ||
opcode == ValueOpcode::LoadBufferU32 ||
@@ -3372,7 +3372,7 @@ private:
!memory.SupportsIndirectBufferLoad(op)) {
Fail(flags.pc,
"buffer descriptor is not a valid runtime value; GPU-selected access "
"requires a scalar, raw DWORD x1/x2/x3/x4, or formatted X load");
"requires a scalar, raw 8/16-bit, raw DWORD x1/x2/x3/x4, or formatted X load");
}
m_program.memory_info[flags.index].kind = ResourceKind::IndirectBuffer;
m_info.uses_dma = true;