1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
|
#include "shader-cursor.h"
namespace gfx
{
Result gfx::ShaderCursor::getDereferenced(ShaderCursor& outCursor) const
{
switch (m_typeLayout->getKind())
{
default:
return SLANG_E_INVALID_ARG;
case slang::TypeReflection::Kind::ConstantBuffer:
case slang::TypeReflection::Kind::ParameterBlock:
{
auto subObject = m_baseObject->getObject(m_offset);
outCursor = ShaderCursor(subObject);
return SLANG_OK;
}
}
}
ShaderCursor ShaderCursor::getExplicitCounter() const
{
// Similar to getField below
// The alternative to handling this here would be to augment IResourceView
// with a `getCounterResourceView()`, and set that also in `setResource`
if(const auto counterVarLayout = m_typeLayout->getExplicitCounter())
{
ShaderCursor counterCursor;
// The counter cursor will point into the same parent object.
counterCursor.m_baseObject = m_baseObject;
// The type being pointed to is the type of the field.
counterCursor.m_typeLayout = counterVarLayout->getTypeLayout();
// The byte offset is the current offset plus the relative offset of the counter.
// The offset in binding ranges is computed similarly.
counterCursor.m_offset.uniformOffset
= m_offset.uniformOffset + SlangInt(counterVarLayout->getOffset());
counterCursor.m_offset.bindingRangeIndex
= m_offset.bindingRangeIndex + GfxIndex(m_typeLayout->getExplicitCounterBindingRangeOffset());
// The index of the counter within any binding ranges will be the same
// as the index computed for the parent structure.
//
// Note: this case would arise for an array of structured buffers
//
// AppendStructuredBuffer g[4];
//
// In this scenario, `g` holds two binding ranges:
//
// * Range #0 comprises 4 element buffers, representing `g[...].elements`
// * Range #1 comprises 4 counter buffers, representing `g[...].counter`
//
// A cursor for `g[2]` would have a `bindingRangeIndex` of zero but
// a `bindingArrayIndex` of 2, indicating that we could end up
// referencing either range, but no matter what we know the index
// is 2. Thus when we form a cursor for `g[2].counter` we want to
// apply the binding range offset to get a `bindingRangeIndex` of
// 1, while the `bindingArrayIndex` is unmodified.
//
// The result is that `g[2].counter` is stored in range #1 at array index 2.
//
counterCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex;
return counterCursor;
}
// Otherwise, return an invalid cursor
return ShaderCursor{};
}
Result ShaderCursor::getField(const char* name, const char* nameEnd, ShaderCursor& outCursor) const
{
// If this cursor is invalid, then can't possible fetch a field.
//
if (!isValid())
return SLANG_E_INVALID_ARG;
// If the cursor is valid, we want to consider the type of data
// it is referencing.
//
switch (m_typeLayout->getKind())
{
// The easy/expected case is when the value has a structure type.
//
case slang::TypeReflection::Kind::Struct:
{
// We start by looking up the index of a field matching `name`.
//
// If there is no such field, we have an error.
//
SlangInt fieldIndex = m_typeLayout->findFieldIndexByName(name, nameEnd);
if (fieldIndex == -1)
break;
// Once we know the index of the field being referenced,
// we create a cursor to point at the field, based on
// the offset information already in this cursor, plus
// offsets derived from the field's layout.
//
slang::VariableLayoutReflection* fieldLayout =
m_typeLayout->getFieldByIndex((unsigned int)fieldIndex);
ShaderCursor fieldCursor;
// The field cursorwill point into the same parent object.
//
fieldCursor.m_baseObject = m_baseObject;
// The type being pointed to is the tyep of the field.
//
fieldCursor.m_typeLayout = fieldLayout->getTypeLayout();
// The byte offset is the current offset plus the relative offset of the field.
// The offset in binding ranges is computed similarly.
//
fieldCursor.m_offset.uniformOffset = m_offset.uniformOffset + fieldLayout->getOffset();
fieldCursor.m_offset.bindingRangeIndex =
m_offset.bindingRangeIndex + (GfxIndex)m_typeLayout->getFieldBindingRangeOffset(fieldIndex);
// The index of the field within any binding ranges will be the same
// as the index computed for the parent structure.
//
// Note: this case would arise for an array of structures with texture-type
// fields. Suppose we have:
//
// struct S { Texture2D t; Texture2D u; }
// S g[4];
//
// In this scenario, `g` holds two binding ranges:
//
// * Range #0 comprises 4 textures, representing `g[...].t`
// * Range #1 comprises 4 textures, representing `g[...].u`
//
// A cursor for `g[2]` would have a `bindingRangeIndex` of zero but
// a `bindingArrayIndex` of 2, iindicating that we could end up
// referencing either range, but no matter what we know the index
// is 2. Thus when we form a cursor for `g[2].u` we want to
// apply the binding range offset to get a `bindingRangeIndex` of
// 1, while the `bindingArrayIndex` is unmodified.
//
// The result is that `g[2].u` is stored in range #1 at array index 2.
//
fieldCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex;
outCursor = fieldCursor;
return SLANG_OK;
}
break;
// In some cases the user might be trying to acess a field by name
// from a cursor that references a constant buffer or parameter block,
// and in these cases we want the access to Just Work.
//
case slang::TypeReflection::Kind::ConstantBuffer:
case slang::TypeReflection::Kind::ParameterBlock:
{
// We basically need to "dereference" the current cursor
// to go from a pointer to a constant buffer to a pointer
// to the *contents* of the constant buffer.
//
ShaderCursor d = getDereferenced();
return d.getField(name, nameEnd, outCursor);
}
break;
}
// If a cursor is pointing at a root shader object (created for a
// program), then we will also iterate over the entry point shader
// objects attached to it and look for a matching parameter name
// on them.
//
// This is a bit of "do what I mean" logic and could potentially
// lead to problems if there could be multiple entry points with
// the same parameter name.
//
// TODO: figure out whether we should support this long-term.
//
auto entryPointCount = (GfxIndex) m_baseObject->getEntryPointCount();
for( GfxIndex e = 0; e < entryPointCount; ++e )
{
ComPtr<IShaderObject> entryPoint;
m_baseObject->getEntryPoint(e, entryPoint.writeRef());
ShaderCursor entryPointCursor(entryPoint);
auto result = entryPointCursor.getField(name, nameEnd, outCursor);
if(SLANG_SUCCEEDED(result))
return result;
}
return SLANG_E_INVALID_ARG;
}
ShaderCursor ShaderCursor::getElement(GfxIndex index) const
{
if (m_containerType != ShaderObjectContainerType::None)
{
ShaderCursor elementCursor;
elementCursor.m_baseObject = m_baseObject;
elementCursor.m_typeLayout = m_typeLayout->getElementTypeLayout();
elementCursor.m_containerType = m_containerType;
elementCursor.m_offset.uniformOffset = index * m_typeLayout->getStride();
elementCursor.m_offset.bindingRangeIndex = 0;
elementCursor.m_offset.bindingArrayIndex = index;
return elementCursor;
}
switch( m_typeLayout->getKind() )
{
case slang::TypeReflection::Kind::Array:
{
ShaderCursor elementCursor;
elementCursor.m_baseObject = m_baseObject;
elementCursor.m_typeLayout = m_typeLayout->getElementTypeLayout();
elementCursor.m_offset.uniformOffset =
m_offset.uniformOffset +
index * m_typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM);
elementCursor.m_offset.bindingRangeIndex = m_offset.bindingRangeIndex;
elementCursor.m_offset.bindingArrayIndex =
m_offset.bindingArrayIndex * (GfxCount)m_typeLayout->getElementCount() + index;
return elementCursor;
}
break;
case slang::TypeReflection::Kind::Struct:
{
// The logic here is similar to `getField()` except that we don't
// need to look up the field index based on a name first.
//
auto fieldIndex = index;
slang::VariableLayoutReflection* fieldLayout =
m_typeLayout->getFieldByIndex((unsigned int)fieldIndex);
if(!fieldLayout)
return ShaderCursor();
ShaderCursor fieldCursor;
fieldCursor.m_baseObject = m_baseObject;
fieldCursor.m_typeLayout = fieldLayout->getTypeLayout();
fieldCursor.m_offset.uniformOffset = m_offset.uniformOffset + fieldLayout->getOffset();
fieldCursor.m_offset.bindingRangeIndex =
m_offset.bindingRangeIndex + (GfxIndex)m_typeLayout->getFieldBindingRangeOffset(fieldIndex);
fieldCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex;
return fieldCursor;
}
break;
case slang::TypeReflection::Kind::Vector:
case slang::TypeReflection::Kind::Matrix:
{
ShaderCursor fieldCursor;
fieldCursor.m_baseObject = m_baseObject;
fieldCursor.m_typeLayout = m_typeLayout->getElementTypeLayout();
fieldCursor.m_offset.uniformOffset = m_offset.uniformOffset + m_typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM) * index;
fieldCursor.m_offset.bindingRangeIndex = m_offset.bindingRangeIndex;
fieldCursor.m_offset.bindingArrayIndex = m_offset.bindingArrayIndex;
return fieldCursor;
}
break;
}
return ShaderCursor();
}
static int _peek(const char* slice)
{
const char* b = slice;
if (!b || !*b)
return -1;
return *b;
}
static int _get(const char*& slice)
{
const char* b = slice;
if (!b || !*b)
return -1;
auto result = *b++;
slice = b;
return result;
}
Result ShaderCursor::followPath(const char* path, ShaderCursor& ioCursor)
{
ShaderCursor cursor = ioCursor;
enum
{
ALLOW_NAME = 0x1,
ALLOW_SUBSCRIPT = 0x2,
ALLOW_DOT = 0x4,
};
int state = ALLOW_NAME | ALLOW_SUBSCRIPT;
const char* rest = path;
for (;;)
{
int c = _peek(rest);
if (c == -1)
break;
else if (c == '.')
{
if (!(state & ALLOW_DOT))
return SLANG_E_INVALID_ARG;
_get(rest);
state = ALLOW_NAME;
continue;
}
else if (c == '[')
{
if (!(state & ALLOW_SUBSCRIPT))
return SLANG_E_INVALID_ARG;
_get(rest);
GfxCount index = 0;
while (_peek(rest) != ']')
{
int d = _get(rest);
if (d >= '0' && d <= '9')
{
index = index * 10 + (d - '0');
}
else
{
return SLANG_E_INVALID_ARG;
}
}
if (_peek(rest) != ']')
return SLANG_E_INVALID_ARG;
_get(rest);
cursor = cursor.getElement(index);
state = ALLOW_DOT | ALLOW_SUBSCRIPT;
continue;
}
else
{
const char* nameBegin = rest;
for (;;)
{
switch (_peek(rest))
{
default:
_get(rest);
continue;
case -1:
case '.':
case '[':
break;
}
break;
}
char const* nameEnd = rest;
ShaderCursor newCursor;
cursor.getField(nameBegin, nameEnd, newCursor);
cursor = newCursor;
state = ALLOW_DOT | ALLOW_SUBSCRIPT;
continue;
}
}
ioCursor = cursor;
return SLANG_OK;
}
} // namespace gfx
|