Skip to content

Commit 6d522cb

Browse files
GLSL: Consider hazards with load-store on buffer pointers.
1 parent 5d56c4f commit 6d522cb

7 files changed

Lines changed: 172 additions & 13 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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 float FragColor;
7+
layout(descriptor_heap, std430) readonly buffer SSBONoWrite
8+
{
9+
vec4 data[];
10+
} spvSSBONoWriteResourceHeap[];
11+
12+
layout(descriptor_heap, std430) buffer SSBO
13+
{
14+
vec4 data[];
15+
} spvSSBOResourceHeap[];
16+
17+
18+
void main()
19+
{
20+
spvSSBOResourceHeap[2].data[0].z = 0.0;
21+
FragColor = spvSSBONoWriteResourceHeap[2].data[2].z;
22+
float _34 = spvSSBOResourceHeap[2].data[0].z;
23+
spvSSBOResourceHeap[2].data[0].z = 0.0;
24+
FragColor = _34;
25+
}
26+
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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 Coherent
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_float = OpTypePointer Output %float
36+
%FragColor = OpVariable %_ptr_Output_float Output
37+
%float_0 = OpConstant %float 0
38+
%_ptr_UniformConstant = OpTypeUntypedPointerKHR UniformConstant
39+
%resource_heap = OpUntypedVariableKHR %_ptr_UniformConstant UniformConstant
40+
%int = OpTypeInt 32 1
41+
%int_0 = OpConstant %int 0
42+
%int_2 = OpConstant %int 2
43+
%_runtimearr_v4float = OpTypeRuntimeArray %v4float
44+
%SSBO = OpTypeStruct %_runtimearr_v4float
45+
%_ptr_StorageBuffer = OpTypeUntypedPointerKHR StorageBuffer
46+
%_ptr_StorageBuffer_SSBO = OpTypePointer StorageBuffer %SSBO
47+
%_ptr_StorageBuffer_v4float = OpTypePointer StorageBuffer %v4float
48+
%_ptr_StorageBuffer_float = OpTypePointer StorageBuffer %float
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+
%19 = OpUntypedAccessChainKHR %_ptr_UniformConstant %_runtimearr_20 %resource_heap %int_2
56+
%readonly = OpBufferPointerEXT %_ptr_StorageBuffer %19
57+
%writeonly = OpBufferPointerEXT %_ptr_StorageBuffer %19
58+
59+
%chain_load = OpUntypedAccessChainKHR %_ptr_StorageBuffer %SSBO %readonly %int_0 %int_2
60+
%chain_load_again = OpUntypedAccessChainKHR %_ptr_StorageBuffer %v4float %chain_load %int_2
61+
62+
%chain_store = OpUntypedAccessChainKHR %_ptr_StorageBuffer %SSBO %writeonly %int_0 %int_0
63+
%chain_store_again = OpUntypedAccessChainKHR %_ptr_StorageBuffer %v4float %chain_store %int_2
64+
65+
; Read through different OpBufferPointerEXT. Aliasing is not considered.
66+
%load_readonly = OpLoad %float %chain_load_again
67+
OpStore %chain_store_again %float_0
68+
OpStore %FragColor %load_readonly
69+
70+
; Now read on the same underlying OpBufferPointerEXT. We have to be careful.
71+
%load_rw = OpLoad %float %chain_store_again
72+
OpStore %chain_store_again %float_0
73+
OpStore %FragColor %load_rw
74+
75+
OpReturn
76+
OpFunctionEnd

spirv_common.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,10 @@ struct SPIRExpression : IVariant
817817
// If this expression represents a OpBufferPointerEXT cast.
818818
bool buffer_pointer = false;
819819

820+
// Temporaries which can remain forwarded as long as this variable is not modified.
821+
// Only used for buffer pointers.
822+
SmallVector<ID> buffer_pointer_dependees;
823+
820824
// A list of expressions which this expression depends on.
821825
SmallVector<ID> expression_dependencies;
822826

spirv_cross.cpp

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ SPIRVariable *Compiler::maybe_get_backing_variable(uint32_t chain)
434434
return var;
435435
}
436436

437-
const SPIRExpression *Compiler::maybe_get_backing_buffer_pointer(uint32_t chain) const
437+
SPIRExpression *Compiler::maybe_get_backing_buffer_pointer(uint32_t chain)
438438
{
439439
auto *expr = maybe_get<SPIRExpression>(chain);
440440
while (expr && !expr->buffer_pointer && expr->loaded_from)
@@ -446,6 +446,7 @@ void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded)
446446
{
447447
auto &e = get<SPIRExpression>(expr);
448448
auto *var = maybe_get_backing_variable(chain);
449+
auto *buffer_pointer = maybe_get_backing_buffer_pointer(chain);
449450

450451
if (var)
451452
{
@@ -460,6 +461,13 @@ void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded)
460461
if (var && var->parameter)
461462
var->parameter->read_count++;
462463
}
464+
else if (buffer_pointer)
465+
{
466+
e.loaded_from = buffer_pointer->self;
467+
// If the backing variable is immutable, we do not need to depend on the variable.
468+
if (forwarded && !is_immutable(buffer_pointer->self))
469+
buffer_pointer->buffer_pointer_dependees.push_back(e.self);
470+
}
463471
}
464472

465473
void Compiler::register_write(uint32_t chain)
@@ -477,6 +485,8 @@ void Compiler::register_write(uint32_t chain)
477485
var = maybe_get<SPIRVariable>(access_chain->loaded_from);
478486
}
479487

488+
auto *buffer_pointer = maybe_get_backing_buffer_pointer(chain);
489+
480490
auto &chain_type = expression_type(chain);
481491

482492
if (var)
@@ -487,12 +497,7 @@ void Compiler::register_write(uint32_t chain)
487497
// If our variable is in a storage class which can alias with other buffers,
488498
// invalidate all variables which depend on aliased variables. And if this is a
489499
// variable pointer, then invalidate all variables regardless.
490-
if (BuiltIn(get_decoration(var->self, DecorationBuiltIn)) == BuiltInResourceHeapEXT)
491-
{
492-
// Very free form aliasing, be conservative.
493-
flush_all_active_variables();
494-
}
495-
else if (get_variable_data_type(*var).pointer)
500+
if (get_variable_data_type(*var).pointer)
496501
{
497502
flush_all_active_variables();
498503

@@ -526,6 +531,10 @@ void Compiler::register_write(uint32_t chain)
526531
force_recompile();
527532
}
528533
}
534+
else if (buffer_pointer)
535+
{
536+
flush_dependees(*buffer_pointer);
537+
}
529538
else if (chain_type.pointer)
530539
{
531540
// If we stored through a variable pointer, then we don't know which
@@ -547,6 +556,16 @@ void Compiler::flush_dependees(SPIRVariable &var)
547556
var.dependees.clear();
548557
}
549558

559+
void Compiler::flush_dependees(SPIRExpression &expr)
560+
{
561+
// A little ugly to split things up like this since BufferPointerEXT is a weird case
562+
// where it's both an expression (chain into global heap) and a memory declaration at the same time ...
563+
assert(expr.buffer_pointer);
564+
for (auto dep : expr.buffer_pointer_dependees)
565+
invalid_expressions.insert(dep);
566+
expr.buffer_pointer_dependees.clear();
567+
}
568+
550569
void Compiler::flush_all_aliased_variables()
551570
{
552571
for (auto aliased : aliased_variables)
@@ -557,6 +576,8 @@ void Compiler::flush_all_atomic_capable_variables()
557576
{
558577
for (auto global : global_variables)
559578
flush_dependees(get<SPIRVariable>(global));
579+
for (auto global : buffer_pointer_variables)
580+
flush_dependees(get<SPIRExpression>(global));
560581
flush_all_aliased_variables();
561582
}
562583

@@ -578,6 +599,8 @@ void Compiler::flush_all_active_variables()
578599
flush_dependees(get<SPIRVariable>(arg.id));
579600
for (auto global : global_variables)
580601
flush_dependees(get<SPIRVariable>(global));
602+
for (auto global : buffer_pointer_variables)
603+
flush_dependees(get<SPIRExpression>(global));
581604

582605
flush_all_aliased_variables();
583606
}

spirv_cross.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ class Compiler
585585
// (SSBO, image load store, etc)
586586
SmallVector<uint32_t> global_variables;
587587
SmallVector<uint32_t> aliased_variables;
588+
SmallVector<uint32_t> buffer_pointer_variables;
588589

589590
SPIRFunction *current_function = nullptr;
590591
SPIRBlock *current_block = nullptr;
@@ -705,7 +706,7 @@ class Compiler
705706
bool expression_is_lvalue(uint32_t id) const;
706707
bool variable_storage_is_aliased(const SPIRVariable &var);
707708
SPIRVariable *maybe_get_backing_variable(uint32_t chain);
708-
const SPIRExpression *maybe_get_backing_buffer_pointer(uint32_t chain) const;
709+
SPIRExpression *maybe_get_backing_buffer_pointer(uint32_t chain);
709710

710711
void register_read(uint32_t expr, uint32_t chain, bool forwarded);
711712
void register_write(uint32_t chain);
@@ -740,6 +741,7 @@ class Compiler
740741

741742
// Dependency tracking for temporaries read from variables.
742743
void flush_dependees(SPIRVariable &var);
744+
void flush_dependees(SPIRExpression &expr);
743745
void flush_all_active_variables();
744746
void flush_control_dependent_expressions(uint32_t block);
745747
void flush_all_atomic_capable_variables();

spirv_glsl.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ void CompilerGLSL::reset(uint32_t iteration_count)
368368
expression_usage_counts.clear();
369369
forwarded_temporaries.clear();
370370
suppressed_usage_tracking.clear();
371+
buffer_pointer_variables.clear();
371372

372373
// Ensure that we declare phi-variable copies even if the original declaration isn't deferred
373374
flushed_phi_variables.clear();
@@ -8499,20 +8500,25 @@ bool CompilerGLSL::expression_is_constant_null(uint32_t id) const
84998500
return c->constant_is_null();
85008501
}
85018502

8502-
bool CompilerGLSL::expression_is_non_value_type_array(uint32_t ptr)
8503+
bool CompilerGLSL::expression_is_non_value_type_array(uint32_t value_type_id, uint32_t ptr)
85038504
{
8504-
auto &type = expression_type(ptr);
8505-
if (!is_array(get_pointee_type(type)))
8505+
auto &type = get<SPIRType>(value_type_id);
8506+
if (!is_array(type))
85068507
return false;
85078508

85088509
if (!backend.array_is_value_type)
85098510
return true;
85108511

8512+
if (!backend.array_is_value_type_in_buffer_blocks && maybe_get_backing_buffer_pointer(ptr))
8513+
return true;
8514+
85118515
auto *var = maybe_get_backing_variable(ptr);
85128516
if (!var)
85138517
return false;
85148518

85158519
auto &backed_type = get<SPIRType>(var->basetype);
8520+
8521+
// Only consider explicitly laid out types here, not IO blocks.
85168522
return !backend.array_is_value_type_in_buffer_blocks && backed_type.basetype == SPIRType::Struct &&
85178523
has_member_decoration(backed_type.self, 0, DecorationOffset);
85188524
}
@@ -11950,6 +11956,9 @@ bool CompilerGLSL::should_forward(uint32_t id) const
1195011956
if (is_immutable(id))
1195111957
return true;
1195211958

11959+
if (expr && expr->buffer_pointer)
11960+
return true;
11961+
1195311962
return false;
1195411963
}
1195511964

@@ -12038,6 +12047,8 @@ void CompilerGLSL::register_impure_function_call()
1203812047
flush_dependees(get<SPIRVariable>(global));
1203912048
for (auto aliased : aliased_variables)
1204012049
flush_dependees(get<SPIRVariable>(aliased));
12050+
for (auto ptr : buffer_pointer_variables)
12051+
flush_dependees(get<SPIRExpression>(ptr));
1204112052
}
1204212053

1204312054
void CompilerGLSL::register_call_out_argument(uint32_t id)
@@ -12935,7 +12946,7 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
1293512946
bool usage_tracking = flattened && (type.basetype == SPIRType::Struct || (type.columns > 1));
1293612947

1293712948
SPIRExpression *e = nullptr;
12938-
if (!forward && expression_is_non_value_type_array(ptr))
12949+
if (!forward && expression_is_non_value_type_array(result_type, ptr))
1293912950
{
1294012951
// Complicated load case where we need to make a copy of ptr, but we cannot, because
1294112952
// it is an array, and our backend does not support arrays as value types.
@@ -13176,7 +13187,24 @@ void CompilerGLSL::emit_instruction(const Instruction &instruction)
1317613187
expr.buffer_pointer = true;
1317713188
expr.implied_read_expressions = chain_expr->implied_read_expressions;
1317813189
expr.expression_dependencies = chain_expr->expression_dependencies;
13190+
expr.immutable = false;
13191+
13192+
// If the buffer pointer is marked non-writable, ignore alias tracking by flagging the expression as immutable.
13193+
for (auto &heap : descriptor_heap_types)
13194+
{
13195+
if (heap.buffer_pointer_id == result_id)
13196+
{
13197+
if (heap.nonwritable)
13198+
expr.immutable = true;
13199+
break;
13200+
}
13201+
}
13202+
13203+
if (!expr.immutable && ir.get_buffer_block_type_flags(get<SPIRType>(type_id)).get(DecorationNonWritable))
13204+
expr.immutable = true;
1317913205

13206+
// Used for load-store tracking.
13207+
buffer_pointer_variables.push_back(result_id);
1318013208
break;
1318113209
}
1318213210

spirv_glsl.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,7 @@ class CompilerGLSL : public Compiler
10591059
void disallow_forwarding_in_expression_chain(const SPIRExpression &expr);
10601060

10611061
bool expression_is_constant_null(uint32_t id) const;
1062-
bool expression_is_non_value_type_array(uint32_t ptr);
1062+
bool expression_is_non_value_type_array(uint32_t value_type_id, uint32_t ptr);
10631063
virtual void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression);
10641064

10651065
uint32_t get_integer_width_for_instruction(const Instruction &instr) const;

0 commit comments

Comments
 (0)