Skip to content

Commit 27cced8

Browse files
GLSL: Consider that OpBufferPointerEXT can be tagged with RW flags.
1 parent 2196a7e commit 27cced8

6 files changed

Lines changed: 210 additions & 27 deletions
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#version 460
2+
#extension GL_EXT_descriptor_heap : require
3+
#extension GL_EXT_nonuniform_qualifier : require
4+
#extension GL_EXT_shader_image_load_formatted : require
5+
6+
layout(location = 0) out vec4 FragColor;
7+
layout(descriptor_heap, std430) readonly buffer SSBONoWrite
8+
{
9+
vec4 data[];
10+
} spvSSBONoWriteResourceHeap[];
11+
12+
layout(descriptor_heap, std430) writeonly buffer SSBONoRead
13+
{
14+
vec4 data[];
15+
} spvSSBONoReadResourceHeap[];
16+
17+
18+
void main()
19+
{
20+
FragColor = vec4(0.0);
21+
spvSSBONoReadResourceHeap[2].data[0] = spvSSBONoWriteResourceHeap[2].data[2];
22+
FragColor = spvSSBONoWriteResourceHeap[2].data[2];
23+
}
24+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
; SPIR-V
2+
; Version: 1.0
3+
; Generator: Khronos Glslang Reference Front End; 11
4+
; Bound: 31
5+
; Schema: 0
6+
OpCapability Shader
7+
OpCapability UntypedPointersKHR
8+
OpCapability DescriptorHeapEXT
9+
OpExtension "SPV_EXT_descriptor_heap"
10+
OpExtension "SPV_KHR_untyped_pointers"
11+
%1 = OpExtInstImport "GLSL.std.450"
12+
OpMemoryModel Logical GLSL450
13+
OpEntryPoint Fragment %main "main" %FragColor %resource_heap
14+
OpExecutionMode %main OriginUpperLeft
15+
OpSource GLSL 460
16+
OpSourceExtension "GL_EXT_descriptor_heap"
17+
OpSourceExtension "GL_EXT_nonuniform_qualifier"
18+
OpName %main "main"
19+
OpName %FragColor "FragColor"
20+
OpName %resource_heap "resource_heap"
21+
OpName %SSBO "SSBO"
22+
OpMemberName %SSBO 0 "data"
23+
OpDecorate %FragColor Location 0
24+
OpDecorate %resource_heap BuiltIn ResourceHeapEXT
25+
OpDecorate %_runtimearr_v4float ArrayStride 16
26+
OpDecorate %SSBO Block
27+
OpDecorate %readonly NonWritable
28+
OpDecorate %writeonly NonReadable
29+
OpMemberDecorate %SSBO 0 Offset 0
30+
OpDecorateId %_runtimearr_20 ArrayStrideIdEXT %21
31+
%void = OpTypeVoid
32+
%3 = OpTypeFunction %void
33+
%float = OpTypeFloat 32
34+
%v4float = OpTypeVector %float 4
35+
%_ptr_Output_v4float = OpTypePointer Output %v4float
36+
%FragColor = OpVariable %_ptr_Output_v4float Output
37+
%float_0 = OpConstant %float 0
38+
%11 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
39+
%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
40+
%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
41+
%int = OpTypeInt 32 1
42+
%int_0 = OpConstant %int 0
43+
%int_2 = OpConstant %int 2
44+
%_runtimearr_v4float = OpTypeRuntimeArray %v4float
45+
%SSBO = OpTypeStruct %_runtimearr_v4float
46+
%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer
47+
%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO
48+
%_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float
49+
%20 = OpTypeBufferEXT StorageBuffer
50+
%21 = OpConstantSizeOfEXT %int %20
51+
%_runtimearr_20 = OpTypeRuntimeArray %20
52+
%uint = OpTypeInt 32 0
53+
%main = OpFunction %void None %3
54+
%5 = OpLabel
55+
OpStore %FragColor %11
56+
%19 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_20 %resource_heap %int_2
57+
%readonly = OpBufferPointerEXT %_ptr_StorageBuffer_SSBO %19
58+
%writeonly = OpBufferPointerEXT %_ptr_StorageBuffer_SSBO %19
59+
%chain_load = OpAccessChain %_ptr_StorageBuffer_v4float %readonly %int_0 %int_2
60+
%chain_store = OpAccessChain %_ptr_StorageBuffer_v4float %writeonly %int_0 %int_0
61+
%loaded = OpLoad %v4float %chain_load
62+
OpStore %chain_store %loaded
63+
OpStore %FragColor %loaded
64+
OpReturn
65+
OpFunctionEnd

spirv_cross.cpp

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5453,7 +5453,15 @@ void Compiler::analyze_descriptor_heap_types()
54535453
// BufferPointerEXT can return untyped or typed pointers.
54545454
// If it's typed, we resolve it here.
54555455
if (ptr_type.basetype == SPIRType::Struct)
5456-
add_unique_type(ptr_type.self, ptr_type.storage);
5456+
{
5457+
DescriptorHeapMeta meta = {};
5458+
meta.type = ptr_type.self;
5459+
meta.buffer_pointer_id = args[1];
5460+
meta.storage = ptr_type.storage;
5461+
meta.nonreadable = compiler.has_decoration(args[1], DecorationNonReadable);
5462+
meta.nonwritable = compiler.has_decoration(args[1], DecorationNonWritable);
5463+
add_unique_type(meta);
5464+
}
54575465
buffer_pointers[args[1]] = args[0];
54585466
break;
54595467
}
@@ -5523,7 +5531,9 @@ void Compiler::analyze_descriptor_heap_types()
55235531
data_type.basetype == SPIRType::AccelerationStructure ||
55245532
data_type.basetype == SPIRType::Sampler)
55255533
{
5526-
add_unique_type(data_type.self, StorageClassUniformConstant);
5534+
DescriptorHeapMeta meta = {};
5535+
meta.type = data_type.self;
5536+
add_unique_type(meta);
55275537
}
55285538
else if (buffer_pointers.count(args[3]) != 0)
55295539
{
@@ -5537,7 +5547,13 @@ void Compiler::analyze_descriptor_heap_types()
55375547
if (buffer_type.basetype == SPIRType::Void)
55385548
{
55395549
// This is where the pointer becomes typed, so register it here.
5540-
add_unique_type(data_type.self, buffer_type.storage);
5550+
DescriptorHeapMeta meta = {};
5551+
meta.type = data_type.self;
5552+
meta.buffer_pointer_id = args[3];
5553+
meta.storage = buffer_type.storage;
5554+
meta.nonreadable = compiler.has_decoration(args[3], DecorationNonReadable);
5555+
meta.nonwritable = compiler.has_decoration(args[3], DecorationNonWritable);
5556+
add_unique_type(meta);
55415557
}
55425558
}
55435559
break;
@@ -5552,18 +5568,24 @@ void Compiler::analyze_descriptor_heap_types()
55525568

55535569
explicit HeapHandler(Compiler &compiler_) : OpcodeHandler(compiler_) {}
55545570

5555-
std::vector<std::pair<uint32_t, StorageClass>> heap_types;
5571+
std::vector<DescriptorHeapMeta> heap_types;
55565572
std::unordered_map<uint32_t, TypeID> buffer_pointers;
55575573

5558-
void add_unique_type(uint32_t id, StorageClass storage)
5574+
void add_unique_type(const DescriptorHeapMeta &meta)
55595575
{
5560-
assert(id != 0);
5576+
assert(meta.type != 0);
55615577

55625578
for (auto &type : heap_types)
5563-
if (type.first == id)
5579+
{
5580+
if (type.type == meta.type && type.storage == meta.storage &&
5581+
type.buffer_pointer_id == meta.buffer_pointer_id &&
5582+
type.nonreadable == meta.nonreadable && type.nonwritable == meta.nonwritable)
5583+
{
55645584
return;
5585+
}
5586+
}
55655587

5566-
heap_types.emplace_back(id, storage);
5588+
heap_types.push_back(meta);
55675589
}
55685590
};
55695591

spirv_cross.hpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,17 @@ class Compiler
10901090
SmallVector<uint32_t> physical_storage_non_block_pointer_types;
10911091
std::unordered_map<uint32_t, PhysicalBlockMeta> physical_storage_type_to_alignment;
10921092

1093-
std::vector<std::pair<uint32_t, StorageClass>> descriptor_heap_types;
1093+
struct DescriptorHeapMeta
1094+
{
1095+
TypeID type;
1096+
1097+
// For buffers
1098+
ID buffer_pointer_id;
1099+
StorageClass storage;
1100+
bool nonwritable;
1101+
bool nonreadable;
1102+
};
1103+
std::vector<DescriptorHeapMeta> descriptor_heap_types;
10941104
void analyze_descriptor_heap_types();
10951105

10961106
void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler);

spirv_glsl.cpp

Lines changed: 78 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2595,14 +2595,44 @@ void CompilerGLSL::emit_buffer_reference_block(uint32_t type_id, bool forward_de
25952595
}
25962596
}
25972597

2598-
void CompilerGLSL::emit_buffer_block_native(const SPIRVariable *var, SPIRType *type, StorageClass storage)
2598+
std::string CompilerGLSL::to_buffer_pointer_name_prefix(uint32_t ptr_id) const
25992599
{
2600-
if (!type)
2600+
auto itr = std::find_if(descriptor_heap_types.begin(), descriptor_heap_types.end(),
2601+
[&](const DescriptorHeapMeta &meta) { return meta.buffer_pointer_id == ptr_id; });
2602+
2603+
assert(itr != descriptor_heap_types.end());
2604+
2605+
auto name = to_name(itr->type);
2606+
2607+
// The same block type can be instantiated with different read-write decorations.
2608+
if (itr->nonreadable)
2609+
name += "NoRead";
2610+
if (itr->nonwritable)
2611+
name += "NoWrite";
2612+
2613+
return join("spv", name);
2614+
}
2615+
2616+
void CompilerGLSL::emit_buffer_block_native(const SPIRVariable *var, const DescriptorHeapMeta *heap_meta)
2617+
{
2618+
assert(var || heap_meta);
2619+
2620+
SPIRType *type;
2621+
if (var)
26012622
type = &get<SPIRType>(var->basetype);
2623+
else
2624+
type = &get<SPIRType>(heap_meta->type);
26022625

26032626
Bitset flags = var ? ir.get_buffer_block_flags(*var) : ir.get_buffer_block_type_flags(*type);
2604-
if (var)
2605-
storage = var->storage;
2627+
auto storage = var ? var->storage : heap_meta->storage;
2628+
2629+
if (heap_meta)
2630+
{
2631+
if (heap_meta->nonreadable)
2632+
flags.set(DecorationNonReadable);
2633+
if (heap_meta->nonwritable)
2634+
flags.set(DecorationNonWritable);
2635+
}
26062636

26072637
bool ssbo = storage == StorageClassStorageBuffer || storage == StorageClassShaderRecordBufferKHR ||
26082638
has_decoration(type->self, DecorationBufferBlock);
@@ -2615,6 +2645,15 @@ void CompilerGLSL::emit_buffer_block_native(const SPIRVariable *var, SPIRType *t
26152645
// Block names should never alias, but from HLSL input they kind of can because block types are reused for UAVs ...
26162646
auto buffer_name = to_name(type->self, false);
26172647

2648+
if (heap_meta)
2649+
{
2650+
// The same block type can be instantiated with different read-write decorations.
2651+
if (heap_meta->nonreadable)
2652+
buffer_name += "NoRead";
2653+
if (heap_meta->nonwritable)
2654+
buffer_name += "NoWrite";
2655+
}
2656+
26182657
auto &block_namespace = ssbo ? block_ssbo_names : block_ubo_names;
26192658

26202659
// Shaders never use the block by interface name, so we don't
@@ -2694,7 +2733,15 @@ void CompilerGLSL::emit_buffer_block_native(const SPIRVariable *var, SPIRType *t
26942733
}
26952734
else
26962735
{
2697-
end_scope_decl(join("spv", to_name(type->self), "ResourceHeap[]"));
2736+
auto name = to_name(type->self);
2737+
2738+
// The same block type can be instantiated with different read-write decorations.
2739+
if (heap_meta->nonreadable)
2740+
name += "NoRead";
2741+
if (heap_meta->nonwritable)
2742+
name += "NoWrite";
2743+
2744+
end_scope_decl(join("spv", name, "ResourceHeap[]"));
26982745
}
26992746

27002747
statement("");
@@ -4147,7 +4194,7 @@ void CompilerGLSL::emit_resources()
41474194

41484195
for (const auto &heap_type : descriptor_heap_types)
41494196
{
4150-
auto &type = get<SPIRType>(heap_type.first);
4197+
auto &type = get<SPIRType>(heap_type.type);
41514198

41524199
if (type.basetype == SPIRType::Image || type.basetype == SPIRType::AccelerationStructure)
41534200
{
@@ -4167,7 +4214,7 @@ void CompilerGLSL::emit_resources()
41674214
}
41684215
else
41694216
{
4170-
emit_buffer_block_native(nullptr, &type, heap_type.second);
4217+
emit_buffer_block_native(nullptr, &heap_type);
41714218
}
41724219
}
41734220

@@ -12946,11 +12993,23 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
1294612993
if (untyped)
1294712994
{
1294812995
auto *var = maybe_get_backing_variable(ptr_id);
12949-
if (!var || !has_decoration(var->self, DecorationBuiltIn) ||
12950-
(BuiltIn(get_decoration(var->self, DecorationBuiltIn)) != BuiltInResourceHeapEXT &&
12951-
BuiltIn(get_decoration(var->self, DecorationBuiltIn)) != BuiltInSamplerHeapEXT))
12996+
12997+
// Chase back to base SPIRExpression.
12998+
auto *expr = maybe_get<SPIRExpression>(ptr_id);
12999+
while (expr && !expr->buffer_pointer && expr->loaded_from)
13000+
expr = maybe_get<SPIRExpression>(expr->loaded_from);
13001+
13002+
// Buffer pointers stop the loaded from chain to deal with aliasing better, so carve that out specifically.
13003+
bool is_buffer_pointer = expr && expr->buffer_pointer;
13004+
13005+
if (!is_buffer_pointer)
1295213006
{
12953-
SPIRV_CROSS_THROW("Untyped pointer access chains are currently only supported for descriptor heap access.");
13007+
if (!var || !has_decoration(var->self, DecorationBuiltIn) ||
13008+
(BuiltIn(get_decoration(var->self, DecorationBuiltIn)) != BuiltInResourceHeapEXT &&
13009+
BuiltIn(get_decoration(var->self, DecorationBuiltIn)) != BuiltInSamplerHeapEXT))
13010+
{
13011+
SPIRV_CROSS_THROW("Untyped pointer access chains are currently only supported for descriptor heap access.");
13012+
}
1295413013
}
1295513014
}
1295613015

@@ -12978,7 +13037,10 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
1297813037
// For further buffer access chains, we don't do any fixups since we have resolved to proper types.
1297913038
// For buffer types we only prepend when the access chain starts from a BufferPointerEXT base.
1298013039
// Multi-stage access chains are not possible for image types.
12981-
e = join("spv", to_name(data_type.self), e);
13040+
if (ptr_expr && ptr_expr->buffer_pointer)
13041+
e = join(to_buffer_pointer_name_prefix(ptr_expr->self), e);
13042+
else
13043+
e = join("spv", to_name(data_type.self), e);
1298213044
}
1298313045
}
1298413046

@@ -13081,10 +13143,9 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
1308113143

1308213144
if (untyped)
1308313145
{
13084-
auto &data_type = get<SPIRType>(ops[2]);
1308513146
auto *ptr_expr = maybe_get<SPIRExpression>(ptr_id);
1308613147
if (ptr_expr && ptr_expr->buffer_pointer)
13087-
e = join("spv", to_name(data_type.self), e);
13148+
e = join(to_buffer_pointer_name_prefix(ptr_expr->self), e);
1308813149
}
1308913150

1309013151
if (has_decoration(ptr_id, DecorationNonUniform))
@@ -13112,10 +13173,11 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
1311213173
// BufferPointerEXT can return a typed pointer, in which case we need to resolve the heap alias now.
1311313174
auto &type = get<SPIRType>(type_id);
1311413175
if (type.basetype == SPIRType::Struct)
13115-
e = join("spv", to_name(type.self), e);
13176+
e = join(to_buffer_pointer_name_prefix(result_id), e);
1311613177

1311713178
auto &expr = set<SPIRExpression>(result_id, std::move(e), type_id, true);
13118-
expr.loaded_from = chain_expr->loaded_from;
13179+
// There isn't any backing variable here. OpBufferPointerEXT is meant to be a memory declaration instruction.
13180+
expr.loaded_from = 0;
1311913181
expr.access_chain = true;
1312013182
expr.buffer_pointer = true;
1312113183
expr.implied_read_expressions = chain_expr->implied_read_expressions;

spirv_glsl.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,8 +678,8 @@ class CompilerGLSL : public Compiler
678678
void emit_extension_workarounds(ExecutionModel model);
679679
void emit_subgroup_arithmetic_workaround(const std::string &func, Op op, GroupOperation group_op);
680680
void emit_polyfills(uint32_t polyfills, bool relaxed);
681-
void emit_buffer_block_native(const SPIRVariable *var, SPIRType *type,
682-
StorageClass storage = StorageClassGeneric);
681+
void emit_buffer_block_native(const SPIRVariable *var, const DescriptorHeapMeta *heap_meta = nullptr);
682+
std::string to_buffer_pointer_name_prefix(uint32_t ptr_id) const;
683683
void emit_buffer_reference_block(uint32_t type_id, bool forward_declaration);
684684
void emit_buffer_block_legacy(const SPIRVariable &var);
685685
void emit_buffer_block_flattened(const SPIRVariable &type);

0 commit comments

Comments
 (0)