Revert "Fix scalar buffer reads across sparse guest mappings"

This reverts commit d7974dc763.
This commit is contained in:
nmzik
2026-10-16 19:09:51 +02:00
parent e7ac242039
commit d01633e18e
13 changed files with 290 additions and 250 deletions
@@ -81,7 +81,8 @@ void ValidateNativeProgram(const IR::Program& program, bool lds_storage) {
}
uses_gds |= memory.kind == IR::ResourceKind::Gds;
uses_lds |= memory.kind == IR::ResourceKind::Lds;
} else if (memory.kind == IR::ResourceKind::Buffer) {
} else if (memory.kind == IR::ResourceKind::Buffer ||
memory.kind == IR::ResourceKind::ScalarBuffer) {
if (memory.resource >= program.info.buffers.size()) {
Fail(program, "typed shader contains an invalid buffer resource");
}
@@ -867,7 +867,7 @@ void StoreFormattedPrepared(ValueEmitContext& ctx, const IR::Inst& inst,
});
}
uint32_t LoadScalarBuffer(ValueEmitContext& ctx, const IR::Inst& inst) {
uint32_t LoadIndirectScalarBuffer(ValueEmitContext& ctx, const IR::Inst& inst) {
auto& state = ctx.state;
const auto& handle = *inst.Arg(0).ResolveInstruction();
const auto word1 = ctx.Arg(handle, 1);
@@ -1545,8 +1545,29 @@ uint32_t EmitReadConst(ValueEmitContext& ctx, const IR::Inst& inst) {
}
void EmitReadConstBuffer(ValueEmitContext& ctx, const IR::Inst& inst) {
if (ctx.Memory(inst).planning_only) return;
ctx.Define(inst, LoadScalarBuffer(ctx, inst));
auto mem = ctx.Memory(inst);
if (mem.planning_only) return;
if (mem.kind == IR::ResourceKind::IndirectBuffer) {
ctx.Define(inst, LoadIndirectScalarBuffer(ctx, inst));
return;
}
auto& state = ctx.state;
mem.kind = IR::ResourceKind::ScalarBuffer;
auto index = Binary(state, spv::OpShiftRightLogical, TypeU32(state), ctx.Arg(inst, 1),
ConstantU32(state, 2));
if (mem.offset >= 4u) {
index = Binary(state, spv::OpIAdd, TypeU32(state), index, ConstantU32(state, mem.offset >> 2u));
}
const auto access = PrepareMemoryResourceAccess(state, mem);
// Scalar buffer addressing aligns the base and each offset independently.
const auto element = Binary(
state, spv::OpIAdd, TypeU32(state), index,
Binary(state, spv::OpShiftRightLogical, TypeU32(state), access.byte_offset, ConstantU32(state, 2)));
const auto condition = EmitMemoryElementInBounds(state, access, element);
ctx.Define(inst, EmitValueOrZeroIfCondition(state, condition, [&]() {
return EmitNative<spv::OpLoad, IR::Type::U32>(
state, EmitMemoryElementPointer(state, access, element));
}));
}
uint32_t EmitGetBufferResourceIndex(ValueEmitContext& ctx, const IR::Inst& inst) {
@@ -269,8 +269,10 @@ MemoryResourceAccess PrepareMemoryResourceAccess(EmitterState& state, const IR::
case IR::ResourceKind::Flat:
case IR::ResourceKind::Global:
EXIT("physical address memory must use the BDA emitter\n");
case IR::ResourceKind::ScalarBuffer:
case IR::ResourceKind::Buffer: {
const auto bits = IR::StorageBufferElementBits(state.program, mem);
const auto bits = mem.kind == IR::ResourceKind::Buffer
? IR::StorageBufferElementBits(state.program, mem) : 32u;
auto* buffer = &state.storage_buffers[2];
if (bits == 8u) {
buffer = &state.storage_buffers[0];
@@ -128,6 +128,7 @@ struct BufferResource {
bool written = false;
bool atomic = false;
bool formatted = false;
bool scalar = false;
bool live = false;
uint32_t indirect_root = NoIndirectBuffer;
uint32_t indirect_mapping_offset = 0;
@@ -1178,7 +1178,7 @@ static UniformFillPlan AnalyzeUniformFill(const Program& program) {
}
if (store == nullptr || visited.size() != program.blocks.size()) return {};
for (const auto& buffer: program.info.buffers) {
if (buffer.read) return {};
if (buffer.read && (!buffer.scalar || buffer.written)) return {};
}
const auto& memory = program.memory_info.at(store->Flags<MemoryFlags>().index);
UniformFillPlan result;
@@ -1589,6 +1589,7 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime
base.formatted ? descriptor.Format() : Prospero::BufferFormat::kInvalid;
buffer.descriptor_swizzle = base.formatted ? descriptor.DstSelXYZW() : DstSel(4, 5, 6, 7);
buffer.zero_stride_oob = descriptor.OutOfBounds() == 0u && stride == 0u;
buffer.scalar_empty = base.scalar && descriptor.GetSize() == 0u;
}
snapshot.images.resize(program.info.images.size());
specialization.images.resize(program.info.images.size());
@@ -1786,6 +1787,17 @@ void ApplyResourceSpecialization(Program& program, const ResourceSpecialization&
}
auto& memory_info = program.memory_info;
// Resolve empty scalar-table words before folding the indirect descriptors that use them.
for (auto* block: program.blocks) {
for (auto& inst: *block) {
if (inst.GetOpcode() != ValueOpcode::ReadConstBuffer) continue;
const auto& memory = memory_info[inst.Flags<MemoryFlags>().index];
if (memory.kind == ResourceKind::ScalarBuffer &&
specialization.buffers[memory.resource].scalar_empty &&
buffers[memory.resource].indirect_root == BufferResource::NoIndirectBuffer)
inst.ReplaceUsesWith(Value(0u));
}
}
const ImageRemap image_remap(specialization);
for (auto* block: program.blocks) {
for (auto it = block->begin(); it != block->end(); ++it) {
@@ -13,6 +13,7 @@ struct ResourceSpecialization {
Prospero::BufferFormat descriptor_format = Prospero::BufferFormat::kInvalid;
uint32_t descriptor_swizzle = DstSel(4, 5, 6, 7);
bool zero_stride_oob = false;
bool scalar_empty = false;
uint32_t indirect_root = BufferResource::NoIndirectBuffer;
uint32_t indirect_mapping_offset = 0;
uint32_t indirect_search_iterations = 0;
@@ -3735,6 +3735,8 @@ private:
resource.written = resource.written || write;
resource.atomic = resource.atomic || atomic;
resource.formatted = resource.formatted || memory.formatted;
resource.scalar = resource.scalar || op == ValueOpcode::ReadConstBuffer ||
memory.kind == ResourceKind::ScalarBuffer;
}
uint32_t AddImage(uint32_t source, const MemoryInfo& memory, ValueOpcode op, uint32_t pc) {
@@ -3881,24 +3883,6 @@ private:
if (memory.planning_only || IsIndirectPlanningMemory(flags.index)) {
return;
}
if (op == ValueOpcode::ReadConstBuffer) {
const auto* handle = inst.Arg(0).Resolve().TryInstruction();
if (memory.kind != ResourceKind::ScalarBuffer || handle == nullptr ||
handle->GetOpcode() != ValueOpcode::GetBufferResource || handle->NumArgs() != 4u) {
Fail(flags.pc, "scalar buffer read requires a four-DWORD buffer descriptor");
}
// Scalar descriptors can span disjoint guest mappings. Keep their raw GPU operands
// separate from handles that vector-resource planning may rewrite.
auto* block = inst.Parent();
const auto where = std::ranges::find_if(block->Instructions(),
[&](const Inst& candidate) { return &candidate == &inst; });
auto& scalar = *block->PrependNewInst(where, ValueOpcode::GetBufferResource,
{handle->Arg(0), handle->Arg(1), handle->Arg(2), handle->Arg(3)});
inst.SetArg(0, Value(&scalar));
m_program.memory_info[flags.index].kind = ResourceKind::IndirectBuffer;
m_info.uses_dma = true;
return;
}
Inst* handle = nullptr;
uint32_t source = 0;
uint32_t resource = 0;
@@ -3910,11 +3894,12 @@ private:
source = indirect->source;
} else if (!GetHandle(inst.Arg(0), ValueOpcode::GetBufferResource, 4, flags.pc,
memory.resource * 4u, handle, source)) {
if (memory.kind != ResourceKind::Buffer ||
if (memory.kind != (op == ValueOpcode::ReadConstBuffer ? ResourceKind::ScalarBuffer
: ResourceKind::Buffer) ||
!memory.SupportsIndirectBufferLoad(op)) {
Fail(flags.pc,
"buffer descriptor is not a valid runtime value; GPU-selected access "
"requires a raw 8/16-bit, 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;
@@ -307,7 +307,7 @@ void Visit(Program& program, ShaderStageInputInfo input_info, InputUsage& inputs
if (shared_access != SharedAccess::None) {
info.uses_lds |= kind == ResourceKind::Lds;
info.uses_gds |= kind == ResourceKind::Gds;
} else if (kind == ResourceKind::Buffer) {
} else if (kind == ResourceKind::Buffer || kind == ResourceKind::ScalarBuffer) {
if (memory.resource >= info.buffers.size()) {
return Fail("buffer operation has invalid resource metadata");
}
@@ -89,8 +89,7 @@ bool IsRawRead(const ResourcePlan& values, const Inst& inst) {
const auto& memory = values.memory_info[index];
if (op != ValueOpcode::LoadBufferU32) {
return (op == ValueOpcode::LoadAddressU32 && memory.kind == ResourceKind::ScalarAddress) ||
(op == ValueOpcode::ReadConstBuffer &&
(memory.kind == ResourceKind::ScalarBuffer || memory.kind == ResourceKind::IndirectBuffer));
(op == ValueOpcode::ReadConstBuffer && memory.kind == ResourceKind::ScalarBuffer);
}
if (inst.NumArgs() != 5u || memory.kind != ResourceKind::Buffer || memory.typed ||
memory.formatted || memory.coherent || memory.data_bits != 32u ||