Skip to content

Commit 7e0c5ad

Browse files
author
Tim Foley
authored
Add Vulkan cross-compilation for byte-address buffers (shader-slang#721)
* Add Vulkan cross-compilation for byte-address buffers This covers `ByteAddressBuffer`, `RWByteAddressBuffer`, and `RasterizerOrderedByteAddressBuffer`. A declaration of any of these types translates to a GLSL `buffer` declaration with a single `uint` array of data. Most of the methods on these types then have straightforward translations to operations on the array. The overall translation is similar to what was already being done for structured buffers. While implementing GLSL translation for the various atomic (`Interlocked*`) methods, I discovered that some of these included declarations that aren't actually included in HLSL. I cleaned these up, including in the declarations of the global `Interlocked*` functions. The test case that is included here covers only the most basic functionality: `Load`, `Load2`, `Load4` and `Store`. We should try to back-fill tests for the remaining methods when we have time. Two large caveats with this work: 1. We don't handle arrays of byte-address buffers, just as we don't handle arrays of structured buffers. That will take additional work. 2. We don't handle byte-address (or structured) buffers being passed as function parameters, since the parameter would need to be declared as a bare `uint[]` array. * Fixup: don't lump raytracing acceleration structures in with buffers Raytracing acceleration structures share a common base class with byte-address buffers (they are both buffer resources without a specific element type), and I was mistakently matching on this base class in an attempt to have a catch-all that applied to all byte-address buffers. The fix here was to add a distinct base class for all byte-address buffers and catch that instead. * Fixup: typos
1 parent 0f67d92 commit 7e0c5ad

7 files changed

+212
-40
lines changed

source/slang/emit.cpp

+71
Original file line numberDiff line numberDiff line change
@@ -5999,6 +5999,66 @@ struct EmitVisitor
59995999
emit(";\n");
60006000
}
60016001

6002+
void emitIRByteAddressBuffer_GLSL(
6003+
EmitContext* ctx,
6004+
IRGlobalVar* varDecl,
6005+
IRByteAddressBufferTypeBase* /* byteAddressBufferType */)
6006+
{
6007+
// TODO: A lot of this logic is copy-pasted from `emitIRStructuredBuffer_GLSL`.
6008+
// It might be worthwhile to share the common code to avoid regressions sneaking
6009+
// in when one or the other, but not both, gets updated.
6010+
6011+
// Shader storage buffer is an OpenGL 430 feature
6012+
//
6013+
// TODO: we should require either the extension or the version...
6014+
requireGLSLVersion(430);
6015+
6016+
Emit("layout(std430");
6017+
6018+
auto layout = getVarLayout(ctx, varDecl);
6019+
if (layout)
6020+
{
6021+
LayoutResourceKind kind = LayoutResourceKind::DescriptorTableSlot;
6022+
EmitVarChain chain(layout);
6023+
6024+
const UInt index = getBindingOffset(&chain, kind);
6025+
const UInt space = getBindingSpace(&chain, kind);
6026+
6027+
Emit(", binding = ");
6028+
Emit(index);
6029+
if (space)
6030+
{
6031+
Emit(", set = ");
6032+
Emit(space);
6033+
}
6034+
}
6035+
6036+
emit(") buffer ");
6037+
6038+
// Generate a dummy name for the block
6039+
emit("_S");
6040+
Emit(ctx->shared->uniqueIDCounter++);
6041+
emit("\n{\n");
6042+
indent();
6043+
6044+
emit("uint ");
6045+
emit(getIRName(varDecl));
6046+
emit("[];\n");
6047+
6048+
dedent();
6049+
emit("}");
6050+
6051+
// TODO: we need to consider the case where the type of the variable is
6052+
// an *array* of structured buffers, in which case we need to declare
6053+
// the block as an array too.
6054+
//
6055+
// The main challenge here is that then the block will have a name,
6056+
// and also the field inside the block will have a name, so that when
6057+
// the user had written `a[i][j]` we now need to emit `a[i].someName[j]`.
6058+
6059+
emit(";\n");
6060+
}
6061+
60026062
void emitIRGlobalVar(
60036063
EmitContext* ctx,
60046064
IRGlobalVar* varDecl)
@@ -6048,6 +6108,17 @@ struct EmitVisitor
60486108
return;
60496109
}
60506110

6111+
// When outputting GLSL, we need to transform any declaration of
6112+
// a `*ByteAddressBuffer<T>` into an ordinary `buffer` declaration.
6113+
if( auto byteAddressBufferType = as<IRByteAddressBufferTypeBase>(unwrapArray(varType)) )
6114+
{
6115+
emitIRByteAddressBuffer_GLSL(
6116+
ctx,
6117+
varDecl,
6118+
byteAddressBufferType);
6119+
return;
6120+
}
6121+
60516122
// We want to skip the declaration of any system-value variables
60526123
// when outputting GLSL (well, except in the case where they
60536124
// actually *require* redeclaration...).

source/slang/hlsl.meta.slang

+44-17
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,28 @@ __magic_type(HLSLByteAddressBufferType)
1818
__intrinsic_type($(kIROp_HLSLByteAddressBufferType))
1919
struct ByteAddressBuffer
2020
{
21+
__target_intrinsic(glsl, "$1 = $0.length()")
2122
void GetDimensions(
2223
out uint dim);
2324

25+
__target_intrinsic(glsl, "$0[$1]")
2426
uint Load(int location);
27+
2528
uint Load(int location, out uint status);
2629

30+
__target_intrinsic(glsl, "uvec2($0[$1], $0[$1+1])")
2731
uint2 Load2(int location);
32+
2833
uint2 Load2(int location, out uint status);
2934

35+
__target_intrinsic(glsl, "uvec3($0[$1], $0[$1+1], $0[$1+2])")
3036
uint3 Load3(int location);
37+
3138
uint3 Load3(int location, out uint status);
3239

40+
__target_intrinsic(glsl, "uvec4($0[$1], $0[$1+1], $0[$1+2], $0[$1+3])")
3341
uint4 Load4(int location);
42+
3443
uint4 Load4(int location, out uint status);
3544
};
3645

@@ -93,112 +102,136 @@ __magic_type(HLSL$(item.name)Type)
93102
__intrinsic_type($(item.op))
94103
struct $(item.name)
95104
{
96-
// Note(tfoley): supports alll operations from `ByteAddressBuffer`
105+
// Note(tfoley): supports all operations from `ByteAddressBuffer`
97106
// TODO(tfoley): can this be made a sub-type?
98107

108+
__target_intrinsic(glsl, "$1 = $0.length()")
99109
void GetDimensions(
100110
out uint dim);
101111

112+
__target_intrinsic(glsl, "$0[$1]")
102113
uint Load(int location);
114+
103115
uint Load(int location, out uint status);
104116

117+
__target_intrinsic(glsl, "uvec2($0[$1], $0[$1+4])")
105118
uint2 Load2(int location);
119+
106120
uint2 Load2(int location, out uint status);
107121

122+
__target_intrinsic(glsl, "uvec3($0[$1], $0[$1+4], $0[$1+8])")
108123
uint3 Load3(int location);
124+
109125
uint3 Load3(int location, out uint status);
110126

127+
__target_intrinsic(glsl, "uvec4($0[$1], $0[$1+4], $0[$1+8], $0[$1+12])")
111128
uint4 Load4(int location);
129+
112130
uint4 Load4(int location, out uint status);
113131

114132
// Added operations:
115133

134+
__target_intrinsic(glsl, "($3 = atomicAdd($0[$1], $2))")
116135
void InterlockedAdd(
117136
UINT dest,
118137
UINT value,
119138
out UINT original_value);
139+
140+
__target_intrinsic(glsl, "atomicAdd($0[$1], $2)")
120141
void InterlockedAdd(
121142
UINT dest,
122143
UINT value);
123144

145+
__target_intrinsic(glsl, "($3 = atomicAnd($0[$1], $2))")
124146
void InterlockedAnd(
125147
UINT dest,
126148
UINT value,
127149
out UINT original_value);
150+
151+
__target_intrinsic(glsl, "atomicAnd($0[$1], $2)")
128152
void InterlockedAnd(
129153
UINT dest,
130154
UINT value);
131155

156+
__target_intrinsic(glsl, "($4 = atomicCompSwap($0[$1], $2, $3))")
132157
void InterlockedCompareExchange(
133158
UINT dest,
134159
UINT compare_value,
135160
UINT value,
136161
out UINT original_value);
137-
void InterlockedCompareExchange(
138-
UINT dest,
139-
UINT compare_value,
140-
UINT value);
141162

163+
__target_intrinsic(glsl, "atomicCompSwap($0[$1], $2, $3)")
142164
void InterlockedCompareStore(
143165
UINT dest,
144166
UINT compare_value,
145167
UINT value);
146-
void InterlockedCompareStore(
147-
UINT dest,
148-
UINT compare_value);
149168

169+
__target_intrinsic(glsl, "($3 = atomicExchange($0[$1], $2))")
150170
void InterlockedExchange(
151171
UINT dest,
152172
UINT value,
153173
out UINT original_value);
154-
void InterlockedExchange(
155-
UINT dest,
156-
UINT value);
157174

175+
__target_intrinsic(glsl, "($3 = atomicMax($0[$1], $2))")
158176
void InterlockedMax(
159177
UINT dest,
160178
UINT value,
161179
out UINT original_value);
180+
181+
__target_intrinsic(glsl, "atomicMax($0[$1], $2)")
162182
void InterlockedMax(
163183
UINT dest,
164184
UINT value);
165185

186+
__target_intrinsic(glsl, "($3 = atomicMin($0[$1], $2))")
166187
void InterlockedMin(
167188
UINT dest,
168189
UINT value,
169190
out UINT original_value);
191+
192+
__target_intrinsic(glsl, "atomicMin($0[$1], $2)")
170193
void InterlockedMin(
171194
UINT dest,
172195
UINT value);
173196

197+
__target_intrinsic(glsl, "($3 = atomicOr($0[$1], $2))")
174198
void InterlockedOr(
175199
UINT dest,
176200
UINT value,
177201
out UINT original_value);
202+
203+
__target_intrinsic(glsl, "atomicOr($0[$1], $2)")
178204
void InterlockedOr(
179205
UINT dest,
180206
UINT value);
181207

208+
__target_intrinsic(glsl, "($3 = atomicXor($0[$1], $2))")
182209
void InterlockedXor(
183210
UINT dest,
184211
UINT value,
185212
out UINT original_value);
213+
214+
__target_intrinsic(glsl, "atomicXor($0[$1], $2)")
186215
void InterlockedXor(
187216
UINT dest,
188217
UINT value);
189218

219+
__target_intrinsic(glsl, "$0[$1] = $2")
190220
void Store(
191221
uint address,
192222
uint value);
193223

224+
__target_intrinsic(glsl, "$0[$1] = $2.x, $0[$1+4] = $2.y")
194225
void Store2(
195226
uint address,
196227
uint2 value);
197228

229+
__target_intrinsic(glsl, "$0[$1] = $2.x, $0[$1+4] = $2.y, $0[$1+8] = $2.z")
198230
void Store3(
199231
uint address,
200232
uint3 value);
201233

234+
__target_intrinsic(glsl, "$0[$1] = $2.x, $0[$1+4] = $2.y, $0[$1+8] = $2.z, $0[$1+12] = $2.w")
202235
void Store4(
203236
uint address,
204237
uint4 value);
@@ -687,12 +720,6 @@ void InterlockedCompareStore(__ref int dest, int compare_value, int value);
687720
__target_intrinsic(glsl, "$atomicCompSwap($A, $1, $2)")
688721
void InterlockedCompareStore(__ref uint dest, uint compare_value, uint value);
689722

690-
__target_intrinsic(glsl, "$atomicExchange($A, $1)")
691-
void InterlockedExchange(__ref int dest, int value);
692-
693-
__target_intrinsic(glsl, "$atomicExchange($A, $1)")
694-
void InterlockedExchange(__ref uint dest, uint value);
695-
696723
__target_intrinsic(glsl, "($2 = $atomicExchange($A, $1))")
697724
void InterlockedExchange(__ref int dest, int value, out int original_value);
698725

0 commit comments

Comments
 (0)