Skip to content

Commit 9c17d0b

Browse files
authored
Support for unbounded array of arrays (shader-slang#1078)
* WIP: Unsized arrays on CPU. * unbounded-array-of-array working on CPU. * Remove some left over comments.
1 parent deeb864 commit 9c17d0b

10 files changed

+262
-20
lines changed

prelude/slang-cpp-types.h

+13-5
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,20 @@ struct FixedArray
2424
T m_data[SIZE];
2525
};
2626

27-
28-
// Hmm... I guess a constant buffer should be unwrapped to be just a struct passed in
29-
/* template <typename T>
30-
struct ConstantBuffer
27+
// An array that has no specified size, becomes a 'Array'. This stores the size so it can potentially
28+
// do bounds checking.
29+
template <typename T>
30+
struct Array
3131
{
32-
}; */
32+
const T& operator[](size_t index) const { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
33+
T& operator[](size_t index) { SLANG_PRELUDE_ASSERT(index < count); return data[index]; }
34+
35+
T* data;
36+
size_t count;
37+
};
38+
39+
/* Constant buffers become a pointer to the contained type, so ConstantBuffer<T> becomes T* in C++ code.
40+
*/
3341

3442
template <typename T, int COUNT>
3543
struct Vector;

source/slang/slang-emit-cpp.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,16 @@ SlangResult CPPSourceEmitter::_calcTypeName(IRType* type, CodeGenTarget target,
544544
out << ", " << elementCount << ">";
545545
return SLANG_OK;
546546
}
547+
case kIROp_UnsizedArrayType:
548+
{
549+
auto arrayType = static_cast<IRUnsizedArrayType*>(type);
550+
auto elementType = arrayType->getElementType();
551+
552+
out << "Array<";
553+
SLANG_RETURN_ON_FAIL(_calcTypeName(elementType, target, out));
554+
out << ">";
555+
return SLANG_OK;
556+
}
547557
default:
548558
{
549559
if (isNominalOp(type->op))

source/slang/slang-type-layout.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,25 @@ struct CPULayoutRulesImpl : DefaultLayoutRulesImpl
366366
}
367367
}
368368

369+
SimpleArrayLayoutInfo GetArrayLayout( SimpleLayoutInfo elementInfo, LayoutSize elementCount) override
370+
{
371+
if (elementCount.isInfinite())
372+
{
373+
// This is an unsized array, get information for element
374+
auto info = Super::GetArrayLayout(elementInfo, LayoutSize(1));
375+
376+
// So it is actually a Array<T> on CPU which is a pointer and a size
377+
info.size = sizeof(void*) * 2;
378+
info.alignment = sizeof(void*);
379+
380+
return info;
381+
}
382+
else
383+
{
384+
return Super::GetArrayLayout(elementInfo, elementCount);
385+
}
386+
}
387+
369388
UniformLayoutInfo BeginStructLayout() override
370389
{
371390
return Super::BeginStructLayout();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//DISABLE_TEST:CPU_REFLECTION: -profile cs_5_0 -entry computeMain -target cpp
2+
//TEST(compute):COMPARE_COMPUTE_EX:-cpu -compute
3+
4+
struct IntAoa { RWStructuredBuffer<int> array[]; }
5+
6+
//TEST_INPUT:ubuffer(data=[0 0 0 0 0 0 0 0], stride=4):dxbinding(0),glbinding(0),out,name outputBuffer
7+
RWStructuredBuffer<int> outputBuffer;
8+
9+
//TEST_INPUT:array(size=2):name g_aoa.array
10+
ParameterBlock<IntAoa> g_aoa;
11+
12+
//TEST_INPUT:ubuffer(data=[1 2 3 4], stride=4):name=g_aoa.array[0]
13+
//TEST_INPUT:ubuffer(data=[8 17 34], stride=4):name=g_aoa.array[1]
14+
15+
[numthreads(8, 1, 1)]
16+
void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
17+
{
18+
int index = int(dispatchThreadID.x);
19+
20+
int baseIndex = index >> 2;
21+
int innerIndex = index & 3;
22+
23+
RWStructuredBuffer<int> buffer = g_aoa.array[baseIndex];
24+
25+
// Get the size
26+
uint bufferCount, bufferStride;
27+
buffer.GetDimensions(bufferCount, bufferStride);
28+
29+
if (innerIndex >= bufferCount)
30+
{
31+
innerIndex = bufferCount - 1;
32+
}
33+
34+
outputBuffer[index] = buffer[innerIndex];
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
1
2+
2
3+
3
4+
4
5+
8
6+
11
7+
22
8+
22

tools/render-test/cpu-compute-util.cpp

+17-2
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,24 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount)
191191
const auto kind = typeLayout->getKind();
192192
switch (kind)
193193
{
194-
case slang::TypeReflection::Kind::Vector:
195-
case slang::TypeReflection::Kind::Matrix:
196194
case slang::TypeReflection::Kind::Array:
195+
{
196+
auto elementCount = int(typeLayout->getElementCount());
197+
if (elementCount == 0)
198+
{
199+
if (srcEntry.type == ShaderInputType::Array)
200+
{
201+
// We need to set the size
202+
CPUMemoryBinding::Buffer buffer;
203+
SLANG_RETURN_ON_FAIL(binding.setArrayCount(location, srcEntry.arrayDesc.size, buffer));
204+
}
205+
break;
206+
}
207+
SLANG_RETURN_ON_FAIL(binding.setInplace(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int)));
208+
break;
209+
}
210+
case slang::TypeReflection::Kind::Vector:
211+
case slang::TypeReflection::Kind::Matrix:
197212
case slang::TypeReflection::Kind::Scalar:
198213
case slang::TypeReflection::Kind::Struct:
199214
{

tools/render-test/cpu-memory-binding.cpp

+128-10
Original file line numberDiff line numberDiff line change
@@ -156,13 +156,25 @@ SlangResult CPUMemoryBinding::_add(slang::VariableLayoutReflection* varLayout, s
156156
{
157157
auto elementTypeLayout = typeLayout->getElementTypeLayout();
158158
auto elementCount = int(typeLayout->getElementCount());
159-
const size_t stride = elementTypeLayout->getSize();
160-
uint8_t* cur = (uint8_t*)dst;
161-
for (int i = 0; i < elementCount; ++i)
159+
160+
if (elementCount == 0)
161+
{
162+
// We don't currently know the size that this array will be. So let's initially size it to 0.
163+
164+
CPPPrelude::Array<uint8_t>& dstBuf = *(CPPPrelude::Array<uint8_t>*)dst;
165+
dstBuf.data = nullptr;
166+
dstBuf.count = 0;
167+
}
168+
else
162169
{
163-
Buffer elementBuffer;
164-
_add(nullptr, elementTypeLayout, cur, elementBuffer);
165-
cur += stride;
170+
const size_t stride = elementTypeLayout->getSize();
171+
uint8_t* cur = (uint8_t*)dst;
172+
for (int i = 0; i < elementCount; ++i)
173+
{
174+
Buffer elementBuffer;
175+
_add(nullptr, elementTypeLayout, cur, elementBuffer);
176+
cur += stride;
177+
}
166178
}
167179
break;
168180
}
@@ -305,6 +317,11 @@ CPUMemoryBinding::Location CPUMemoryBinding::Location::toIndex(int index) const
305317
{
306318
return *this;
307319
}
320+
SLANG_ASSERT(index >= 0);
321+
if (index < 0)
322+
{
323+
return Location();
324+
}
308325

309326
auto typeLayout = m_typeLayout;
310327
uint8_t* cur = m_cur;
@@ -318,20 +335,38 @@ CPUMemoryBinding::Location CPUMemoryBinding::Location::toIndex(int index) const
318335
const auto elementCount = int(typeLayout->getElementCount());
319336
const auto elementStride = typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM);
320337

321-
if (index < 0 || index >= elementCount)
338+
if (elementCount == 0)
322339
{
323-
SLANG_ASSERT(index < elementCount);
340+
CPPPrelude::Array<uint8_t>& array = *(CPPPrelude::Array<uint8_t>*)cur;
341+
if (index < array.count)
342+
{
343+
return Location(elementTypeLayout, array.data + elementStride * index);
344+
}
324345
return Location();
325346
}
326-
327-
return Location(elementTypeLayout, cur + elementStride * index);
347+
else
348+
{
349+
if (index >= elementCount)
350+
{
351+
SLANG_ASSERT(index < elementCount);
352+
return Location();
353+
}
354+
return Location(elementTypeLayout, cur + elementStride * index);
355+
}
328356
}
329357
default: break;
330358
}
331359

332360
return Location();
333361
}
334362

363+
SlangResult CPUMemoryBinding::initValue(slang::TypeLayoutReflection* typeLayout, void* dst)
364+
{
365+
auto size = typeLayout->getSize();
366+
// Zeroing works for built in types, and object types
367+
memset(dst, 0, size);
368+
return SLANG_OK;
369+
}
335370

336371
SlangResult CPUMemoryBinding::setBufferContents(const Location& location, const void* initialData, size_t sizeInBytes)
337372
{
@@ -520,4 +555,87 @@ SlangResult CPUMemoryBinding::setInplace(const Location& location, const void* d
520555
return SLANG_OK;
521556
}
522557

558+
Index CPUMemoryBinding::findBufferIndex(const void* ptr) const
559+
{
560+
const Index count = m_allBuffers.getCount();
561+
562+
for (Index i = 0; i < count; ++i)
563+
{
564+
if (m_allBuffers[i].m_data == ptr)
565+
{
566+
return i;
567+
}
568+
}
569+
return -1;
570+
}
571+
572+
SlangResult CPUMemoryBinding::setArrayCount(const Location& location, int count, Buffer& outBuffer)
573+
{
574+
if (!location.isValid())
575+
{
576+
return SLANG_FAIL;
577+
}
578+
579+
auto typeLayout = location.getTypeLayout();
580+
uint8_t* cur = location.getPtr();
581+
582+
const auto kind = typeLayout->getKind();
583+
584+
if (!(typeLayout->getKind() == slang::TypeReflection::Kind::Array && typeLayout->getElementCount() == 0))
585+
{
586+
return SLANG_FAIL;
587+
}
588+
589+
CPPPrelude::Array<uint8_t>& array = *(CPPPrelude::Array<uint8_t>*)cur;
590+
uint8_t* elements = array.data;
591+
592+
// Making smaller, just reduce the count.
593+
// NOTE! Nothing is done here about deallocating resources which are perhaps no longer reachable.
594+
// This isn't a leakage problem tho, as all buffers are released automatically when scope is left.
595+
if (count <= array.count)
596+
{
597+
array.count = count;
598+
return SLANG_OK;
599+
}
600+
601+
const size_t elementStride = typeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM);
602+
603+
const Index bufferIndex = elements ? findBufferIndex(elements) : -1;
604+
if (bufferIndex > 0)
605+
{
606+
int maxCount = int(m_allBuffers[bufferIndex].m_sizeInBytes / elementStride);
607+
if (count <= maxCount)
608+
{
609+
// Just initialize the space
610+
memset(elements + elementStride * array.count, 0, (count - array.count) * elementStride);
611+
array.count = count;
612+
return SLANG_OK;
613+
}
614+
}
615+
616+
// Ok allocate a buffer that can hold all the elements
617+
618+
const size_t newBufferSize = count * elementStride;
619+
Buffer newBuffer = _allocateBuffer(newBufferSize);
620+
621+
// Copy over the data from the old buffer if there is any
622+
if (elements && array.count)
623+
{
624+
memcpy(newBuffer.m_data, elements, array.count * elementStride);
625+
}
626+
627+
// Remove the old buffer as no longer needed
628+
if (bufferIndex >= 0)
629+
{
630+
m_allBuffers.removeAt(bufferIndex);
631+
}
632+
633+
// Set data
634+
array.count = count;
635+
array.data = newBuffer.m_data;
636+
637+
outBuffer = newBuffer;
638+
return SLANG_OK;
639+
}
640+
523641
} // renderer_test

tools/render-test/cpu-memory-binding.h

+11
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,24 @@ struct CPUMemoryBinding
4343
slang::VariableLayoutReflection* getParameterByName(const char* name);
4444
slang::VariableLayoutReflection* getEntryPointParameterByName(const char* name);
4545

46+
/// Finds which buffer starts at the ptr index
47+
Slang::Index findBufferIndex(const void* ptr) const;
4648

4749
Location find(const char* name);
4850

4951
SlangResult setBufferContents(const Location& location, const void* initialData, size_t sizeInBytes);
5052
SlangResult setNewBuffer(const Location& location, const void* initialData, size_t sizeInBytes, Buffer& outBuffer);
5153
SlangResult setObject(const Location& location, void* object);
5254
SlangResult setInplace(const Location& location, const void* data, size_t sizeInBytes);
55+
/// Initialize memory with a 'sensible' value based on type. Pointer types become null.
56+
SlangResult initValue(slang::TypeLayoutReflection* typeLayout, void* dst);
57+
SlangResult initValue(const Location& location) { return initValue(location.getTypeLayout(), location.getPtr()); }
58+
59+
/// Set the size of a *non fixed size* array at location.
60+
/// A non fixed size array is reflected as having a count of 0 elements.
61+
/// Only returns a buffer in outBuffer if a new buffer is created.
62+
SlangResult setArrayCount(const Location& location, int count, Buffer& outBuffer);
63+
5364
SlangResult init(slang::ShaderReflection* reflection, int entryPointIndex);
5465
CPUMemoryBinding();
5566

tools/render-test/shader-input-layout.cpp

+14-2
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,11 @@ namespace renderer_test
138138
{
139139
ShaderInputLayoutEntry entry;
140140

141-
if (parser.LookAhead("cbuffer"))
141+
if (parser.LookAhead("array"))
142+
{
143+
entry.type = ShaderInputType::Array;
144+
}
145+
else if (parser.LookAhead("cbuffer"))
142146
{
143147
entry.type = ShaderInputType::Buffer;
144148
entry.bufferDesc.type = InputBufferType::ConstantBuffer;
@@ -253,7 +257,15 @@ namespace renderer_test
253257
else if (word == "size")
254258
{
255259
parser.Read("=");
256-
entry.textureDesc.size = parser.ReadInt();
260+
auto size = parser.ReadInt();
261+
if (entry.type == ShaderInputType::Array)
262+
{
263+
entry.arrayDesc.size = size;
264+
}
265+
else
266+
{
267+
entry.textureDesc.size = size;
268+
}
257269
}
258270
else if (word == "random")
259271
{

0 commit comments

Comments
 (0)