Fix NVN buffer descriptor mapping (#363)

Select global-memory buffers by their constant-buffer index and descriptor offset, rather than their position in the descriptor list. Preserve balanced GLASM conditionals and correct 64-bit storage exchange types. Keep existing address-load and range-check code. Adapt Eden fe2591888d.
This commit is contained in:
Bruno 2026-10-07 13:03:43 +02:00 committed by GitHub
parent 7b7dc386c8
commit be889d661f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 60 additions and 40 deletions

View File

@ -31,11 +31,13 @@ void StorageOp(EmitContext& ctx, const IR::Value& binding, ScalarU32 offset,
void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std::string_view expr,
std::string_view else_expr = {}) {
const size_t num_buffers{ctx.info.storage_buffers_descriptors.size()};
size_t num_used_buffers{};
for (size_t index = 0; index < num_buffers; ++index) {
if (!ctx.info.nvn_buffer_used[index]) {
const auto& ssbo{ctx.info.storage_buffers_descriptors[index]};
if (!IsUsedNvnStorageBufferDescriptor(ctx.info, ssbo)) {
continue;
}
const auto& ssbo{ctx.info.storage_buffers_descriptors[index]};
++num_used_buffers;
const u64 ssbo_align_mask{~(ctx.profile.min_ssbo_alignment - 1U)};
ctx.Add("LDC.U64 DC.x,c{}[{}];" // unaligned_ssbo_addr
"AND.U64 DC.x,DC.x,{};" // ssbo_addr = unaligned_ssbo_addr & ssbo_align_mask
@ -65,7 +67,6 @@ void GlobalStorageOp(EmitContext& ctx, Register address, bool pointer_based, std
if (!else_expr.empty()) {
ctx.Add("{}", else_expr);
}
const size_t num_used_buffers{ctx.info.nvn_buffer_used.count()};
for (size_t index = 0; index < num_used_buffers; ++index) {
ctx.Add("ENDIF;");
}

View File

@ -628,7 +628,7 @@ std::string EmitContext::DefineGlobalMemoryFunctions() {
std::string load_func_128{"uvec4 LoadGlobal128(uint64_t addr){"};
const size_t num_buffers{info.storage_buffers_descriptors.size()};
for (size_t index = 0; index < num_buffers; ++index) {
if (!info.nvn_buffer_used[index]) {
if (!IsUsedNvnStorageBufferDescriptor(info, info.storage_buffers_descriptors[index])) {
continue;
}
define_body(write_func, index, "{0}[uint(addr-{1})>>2]=data;return;}}");

View File

@ -70,7 +70,8 @@ Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value&
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id),
Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) {
if (!ctx.profile.support_descriptor_aliasing) {
LOG_WARNING(Shader_SPIRV, "Descriptor aliasing not supported, this cannot be atomic.");
LOG_WARNING(Shader_SPIRV,
"Storage descriptor aliasing not supported, this cannot be atomic.");
return ctx.ConstantNull(ctx.U64);
}
@ -92,7 +93,8 @@ Id StorageAtomicU64(EmitContext& ctx, const IR::Value& binding, const IR::Value&
Id StorageAtomicU32x2(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset, Id value,
Id (Sirit::Module::*non_atomic_func)(Id, Id, Id)) {
if (!ctx.profile.support_descriptor_aliasing) {
LOG_WARNING(Shader_SPIRV, "Descriptor aliasing not supported, this cannot be atomic.");
LOG_WARNING(Shader_SPIRV,
"Storage descriptor aliasing not supported, this cannot be atomic.");
return ctx.ConstantNull(ctx.U32[2]);
}
@ -295,6 +297,11 @@ Id EmitStorageAtomicXor64(EmitContext& ctx, const IR::Value& binding, const IR::
Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset,
Id value) {
if (!ctx.profile.support_descriptor_aliasing) {
LOG_WARNING(Shader_SPIRV,
"Storage descriptor aliasing not supported, this cannot be atomic.");
return ctx.ConstantNull(ctx.U64);
}
if (ctx.profile.support_int64_atomics) {
const Id pointer{StoragePointer(ctx, ctx.storage_types.U64, &StorageDefinitions::U64,
binding, offset, sizeof(u64))};
@ -305,7 +312,7 @@ Id EmitStorageAtomicExchange64(EmitContext& ctx, const IR::Value& binding, const
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
binding, offset, sizeof(u32[2]))};
const Id original{ctx.OpBitcast(ctx.U64, ctx.OpLoad(ctx.U32[2], pointer))};
ctx.OpStore(pointer, value);
ctx.OpStore(pointer, ctx.OpBitcast(ctx.U32[2], value));
return original;
}
@ -351,6 +358,11 @@ Id EmitStorageAtomicXor32x2(EmitContext& ctx, const IR::Value& binding, const IR
Id EmitStorageAtomicExchange32x2(EmitContext& ctx, const IR::Value& binding,
const IR::Value& offset, Id value) {
if (!ctx.profile.support_descriptor_aliasing) {
LOG_WARNING(Shader_SPIRV,
"Storage descriptor aliasing not supported, this cannot be atomic.");
return ctx.ConstantNull(ctx.U32[2]);
}
LOG_WARNING(Shader_SPIRV, "Int64 atomics not supported, fallback to non-atomic");
const Id pointer{StoragePointer(ctx, ctx.storage_types.U32x2, &StorageDefinitions::U32x2,
binding, offset, sizeof(u32[2]))};

View File

@ -941,7 +941,7 @@ void EmitContext::DefineGlobalMemoryFunctions(const Info& info) {
AddLabel();
const size_t num_buffers{info.storage_buffers_descriptors.size()};
for (size_t index = 0; index < num_buffers; ++index) {
if (!info.nvn_buffer_used[index]) {
if (!IsUsedNvnStorageBufferDescriptor(info, info.storage_buffers_descriptors[index])) {
continue;
}
const auto& ssbo{info.storage_buffers_descriptors[index]};

View File

@ -89,43 +89,26 @@ void AddNVNStorageBuffers(IR::Program& program) {
if (!program.info.uses_global_memory) {
return;
}
const u32 driver_cbuf{0};
const u32 descriptor_size{0x10};
const u32 num_buffers{16};
const u32 base{[&] {
switch (program.stage) {
case Stage::VertexA:
case Stage::VertexB:
return 0x110u;
case Stage::TessellationControl:
return 0x210u;
case Stage::TessellationEval:
return 0x310u;
case Stage::Geometry:
return 0x410u;
case Stage::Fragment:
return 0x510u;
case Stage::Compute:
return 0x310u;
}
throw InvalidArgument("Invalid stage {}", program.stage);
}()};
auto& descs{program.info.storage_buffers_descriptors};
for (u32 index = 0; index < num_buffers; ++index) {
if (!program.info.nvn_buffer_used[index]) {
auto& info{program.info};
auto& descs{info.storage_buffers_descriptors};
for (size_t nvn_index = 0; nvn_index < info.nvn_buffer_used.size(); ++nvn_index) {
if (!info.nvn_buffer_used[nvn_index]) {
continue;
}
const u32 offset{base + index * descriptor_size};
const auto it{std::ranges::find(descs, offset, &StorageBufferDescriptor::cbuf_offset)};
const u32 offset{NvnStorageBufferOffset(info, nvn_index)};
const auto it{std::ranges::find_if(descs, [offset](const StorageBufferDescriptor& desc) {
return desc.cbuf_index == Info::NVN_STORAGE_BUFFER_CBUF_INDEX &&
desc.cbuf_offset == offset;
})};
if (it != descs.end()) {
it->is_written |= program.info.stores_global_memory;
it->is_written |= info.stores_global_memory;
continue;
}
descs.push_back({
.cbuf_index = driver_cbuf,
.cbuf_index = Info::NVN_STORAGE_BUFFER_CBUF_INDEX,
.cbuf_offset = offset,
.count = 1,
.is_written = program.info.stores_global_memory,
.is_written = info.stores_global_memory,
});
}
}

View File

@ -107,8 +107,9 @@ void CheckCBufNVN(Info& info, IR::Inst& inst) {
return;
}
const u32 offset{cbuf_offset.U32()};
const u32 descriptor_size{0x10};
const u32 upper_limit{info.nvn_buffer_base + descriptor_size * 16};
const u32 descriptor_size{Info::NVN_STORAGE_BUFFER_DESCRIPTOR_SIZE};
const u32 upper_limit{info.nvn_buffer_base +
descriptor_size * static_cast<u32>(Info::NUM_NVN_STORAGE_BUFFERS)};
if (offset >= info.nvn_buffer_base && offset < upper_limit) {
const std::size_t nvn_index{(offset - info.nvn_buffer_base) / descriptor_size};
info.nvn_buffer_used.set(nvn_index, true);
@ -723,6 +724,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::StorageAtomicAnd64:
case IR::Opcode::StorageAtomicOr64:
case IR::Opcode::StorageAtomicXor64:
case IR::Opcode::StorageAtomicExchange64:
info.used_storage_buffer_types |= IR::Type::U64 | IR::Type::U32x2;
info.uses_int64_bit_atomics = true;
break;

View File

@ -239,6 +239,9 @@ struct Info {
static constexpr size_t MAX_INDIRECT_CBUFS{14};
static constexpr size_t MAX_CBUFS{18};
static constexpr size_t MAX_SSBOS{32};
static constexpr size_t NUM_NVN_STORAGE_BUFFERS{16};
static constexpr u32 NVN_STORAGE_BUFFER_DESCRIPTOR_SIZE{0x10};
static constexpr u32 NVN_STORAGE_BUFFER_CBUF_INDEX{0};
bool uses_workgroup_id{};
bool uses_local_invocation_id{};
@ -319,7 +322,7 @@ struct Info {
u32 constant_buffer_mask{};
std::array<u32, MAX_CBUFS> constant_buffer_used_sizes{};
u32 nvn_buffer_base{};
std::bitset<16> nvn_buffer_used{};
std::bitset<NUM_NVN_STORAGE_BUFFERS> nvn_buffer_used{};
bool requires_layer_emulation{};
IR::Attribute emulated_layer{};
@ -335,6 +338,25 @@ struct Info {
ImageDescriptors image_descriptors;
};
inline u32 NvnStorageBufferOffset(const Info& info, size_t nvn_index) {
return info.nvn_buffer_base +
static_cast<u32>(nvn_index) * Info::NVN_STORAGE_BUFFER_DESCRIPTOR_SIZE;
}
inline bool IsUsedNvnStorageBufferDescriptor(const Info& info,
const StorageBufferDescriptor& desc) {
if (desc.cbuf_index != Info::NVN_STORAGE_BUFFER_CBUF_INDEX ||
desc.cbuf_offset < info.nvn_buffer_base) {
return false;
}
const u32 relative_offset{desc.cbuf_offset - info.nvn_buffer_base};
if (relative_offset % Info::NVN_STORAGE_BUFFER_DESCRIPTOR_SIZE != 0) {
return false;
}
const size_t nvn_index{relative_offset / Info::NVN_STORAGE_BUFFER_DESCRIPTOR_SIZE};
return nvn_index < info.nvn_buffer_used.size() && info.nvn_buffer_used[nvn_index];
}
template <typename Descriptors>
u32 NumDescriptors(const Descriptors& descriptors) {
u32 num{};