summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/slang/compiler.cpp13
-rw-r--r--source/slang/parameter-binding.cpp462
-rw-r--r--source/slang/reflection.cpp19
-rw-r--r--source/slang/type-layout.cpp318
-rw-r--r--source/slang/type-layout.h170
5 files changed, 785 insertions, 197 deletions
diff --git a/source/slang/compiler.cpp b/source/slang/compiler.cpp
index 0f0f3c915..cab6355c1 100644
--- a/source/slang/compiler.cpp
+++ b/source/slang/compiler.cpp
@@ -419,6 +419,19 @@ namespace Slang
break;
}
+ // Some of the `D3DCOMPILE_*` constants aren't available in all
+ // versions of `d3dcompiler.h`, so we define them here just in case
+ #ifndef D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES
+ #define D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES (1 << 20)
+ #endif
+
+ #ifndef D3DCOMPILE_ALL_RESOURCES_BOUND
+ #define D3DCOMPILE_ALL_RESOURCES_BOUND (1 << 21)
+ #endif
+
+ flags |= D3DCOMPILE_ENABLE_STRICTNESS;
+ flags |= D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES;
+
ID3DBlob* codeBlob;
ID3DBlob* diagnosticsBlob;
HRESULT hr = compileFunc(
diff --git a/source/slang/parameter-binding.cpp b/source/slang/parameter-binding.cpp
index e156c9690..bc0f5b760 100644
--- a/source/slang/parameter-binding.cpp
+++ b/source/slang/parameter-binding.cpp
@@ -48,45 +48,165 @@ static bool rangesOverlap(UsedRange const& x, UsedRange const& y)
struct UsedRanges
{
+ // The `ranges` array maintains a sorted list of `UsedRange`
+ // objects such that the `end` of a range is <= the `begin`
+ // of any range that comes after it.
+ //
+ // The values covered by each `[begin,end)` range are marked
+ // as used, and anything not in such an interval is implicitly
+ // free.
+ //
+ // TODO: if it ever starts to matter for performance, we
+ // could encode this information as a tree instead of an array.
+ //
List<UsedRange> ranges;
// Add a range to the set, either by extending
- // an existing range, or by adding a new one...
+ // existing range(s), or by adding a new one.
//
// If we find that the new range overlaps with
// an existing range for a *different* parameter
// then we return that parameter so that the
// caller can issue an error.
- ParameterInfo* Add(UsedRange const& range)
+ //
+ ParameterInfo* Add(UsedRange range)
{
+ // The invariant on entry to this
+ // function is that the `ranges` array
+ // is sorted and no two entries in the
+ // array intersect. We must preserve
+ // that property as a postcondition.
+ //
+ // The other postcondition is that the
+ // interval covered by the input `range`
+ // must be marked as consumed.
+
+ // We will try track any parameter associated
+ // with an overlapping range that doesn't
+ // match the parameter on `range`, so that
+ // the compiler can issue useful diagnostics.
+ //
ParameterInfo* newParam = range.parameter;
ParameterInfo* existingParam = nullptr;
- for (auto& rr : ranges)
+
+ // A clever algorithm might use a binary
+ // search to identify the first entry in `ranges`
+ // that might overlap `range`, but we are going
+ // to settle for being less clever for now, in
+ // the hopes that we can at least be correct.
+ //
+ // Note: we are going to iterate over `ranges`
+ // using indices, because we may actually modify
+ // the array as we go.
+ //
+ Int rangeCount = ranges.Count();
+ for(Int rr = 0; rr < rangeCount; ++rr)
{
- if (rangesOverlap(rr, range)
- && rr.parameter
- && rr.parameter != newParam)
+ auto existingRange = ranges[rr];
+
+ // The invariant on entry to each loop
+ // iteration will be that `range` does
+ // *not* intersect any preceding entry
+ // in the array.
+ //
+ // Note that this invariant might be
+ // true only because we modified
+ // `range` along the way.
+ //
+ // If `range` does not intertsect `existingRange`
+ // then our invariant will be trivially
+ // true for the next iteration.
+ //
+ if(!rangesOverlap(existingRange, range))
{
- // there was an overlap!
- existingParam = rr.parameter;
+ continue;
}
- }
- for (auto& rr : ranges)
- {
- if (rr.begin == range.end)
+ // We now know that `range` and `existingRange`
+ // intersect. The first thing to do
+ // is to check if we have a parameter
+ // associated with `existingRange`, so
+ // that we can use it for emitting diagnostics
+ // about the overlap:
+ //
+ if( existingRange.parameter
+ && existingRange.parameter != newParam)
{
- rr.begin = range.begin;
- return existingParam;
+ // There was an overlap with a range that
+ // had a parameter specified, so we will
+ // use that parameter in any subsequent
+ // diagnostics.
+ //
+ existingParam = existingRange.parameter;
}
- else if (rr.end == range.begin)
+
+ // Before we can move on in our iteration,
+ // we need to re-establish our invariant by modifying
+ // `range` so that it doesn't overlap with `existingRange`.
+ // Of course we also want to end up with a correct
+ // result for the overall operation, so we can't just
+ // throw away intervals.
+ //
+ // We first note that if `range` starts before `existingRange`,
+ // then the interval from `range.begin` to `existingRange.begin`
+ // needs to be accounted for in the final result. Furthermore,
+ // the interval `[range.begin, existingRange.begin)` could not
+ // intersect with any range already in the `ranges` array,
+ // because it comes strictly before `existingRange`, and our
+ // invariant says there is no intersection with preceding ranges.
+ //
+ if(range.begin < existingRange.begin)
{
- rr.end = range.end;
- return existingParam;
+ UsedRange prefix;
+ prefix.begin = range.begin;
+ prefix.end = existingRange.begin;
+ prefix.parameter = range.parameter;
+ ranges.Add(prefix);
}
+ //
+ // Now we know that the interval `[range.begin, existingRange.begin)`
+ // is claimed, if it exists, and clearly the interval
+ // `[existingRange.begin, existingRange.end)` is already claimed,
+ // so the only interval left to consider would be
+ // `[existingRange.end, range.end)`, if it is non-empty.
+ // That range might intersect with others in the array, so
+ // we will need to continue iterating to deal with that
+ // possibility.
+ //
+ range.begin = existingRange.end;
+
+ // If the range would be empty, then of course we have nothing
+ // left to do.
+ //
+ if(range.begin >= range.end)
+ break;
+
+ // Otherwise, have can be sure that `range` now comes
+ // strictly *after* `existingRange`, and thus our invariant
+ // is preserved.
+ }
+
+ // If we manage to exit the loop, then we have resolved
+ // an intersection with existing entries - possibly by
+ // adding some new entries.
+ //
+ // If the `range` we are left with is still non-empty,
+ // then we should go ahead and add it.
+ //
+ if(range.begin < range.end)
+ {
+ ranges.Add(range);
}
- ranges.Add(range);
+
+ // Any ranges that got added along the way might not
+ // be in the proper sorted order, so we'll need to
+ // sort the array to restore our global invariant.
+ //
ranges.Sort();
+
+ // We end by returning an overlapping parameter that
+ // we found along the way, if any.
+ //
return existingParam;
}
@@ -99,6 +219,15 @@ struct UsedRanges
return Add(range);
}
+ ParameterInfo* Add(ParameterInfo* param, UInt begin, LayoutSize end)
+ {
+ UsedRange range;
+ range.parameter = param;
+ range.begin = begin;
+ range.end = end.isFinite() ? end.getFiniteValue() : UInt(-1);
+ return Add(range);
+ }
+
bool contains(UInt index)
{
for (auto rr : ranges)
@@ -152,7 +281,7 @@ struct ParameterBindingInfo
{
size_t space;
size_t index;
- size_t count;
+ LayoutSize count;
};
enum
@@ -1295,7 +1424,7 @@ static void addExplicitParameterBinding(
RefPtr<ParameterInfo> parameterInfo,
VarDeclBase* varDecl,
LayoutSemanticInfo const& semanticInfo,
- UInt count,
+ LayoutSize count,
RefPtr<UsedRangeSet> usedRangeSet = nullptr)
{
auto kind = semanticInfo.kind;
@@ -1384,10 +1513,10 @@ static void addExplicitParameterBindings_HLSL(
// of the given kind.
auto typeRes = typeLayout->FindResourceInfo(kind);
- int count = 0;
+ LayoutSize count = 0;
if (typeRes)
{
- count = (int) typeRes->count;
+ count = typeRes->count;
}
else
{
@@ -1463,7 +1592,7 @@ static void addExplicitParameterBindings_GLSL(
auto count = resInfo->count;
semanticInfo.kind = kind;
- addExplicitParameterBinding(context, parameterInfo, varDecl, semanticInfo, int(count), usedRangeSet);
+ addExplicitParameterBinding(context, parameterInfo, varDecl, semanticInfo, count, usedRangeSet);
}
// Given a single parameter, collect whatever information we have on
@@ -1505,96 +1634,203 @@ static void completeBindingsForParameter(
auto firstVarLayout = parameterInfo->varLayouts.First();
auto firstTypeLayout = firstVarLayout->typeLayout;
+ // We need to deal with allocation of full register spaces first,
+ // since that is the most complicated bit of logic.
+ //
+ // We will compute how many full register spaces the parameter
+ // needs to allocate, across all the kinds of resources it
+ // consumes, so that we can allocate a contiguous range of
+ // spaces.
+ //
+ UInt spacesToAllocateCount = 0;
for(auto typeRes : firstTypeLayout->resourceInfos)
{
- // Did we already apply some explicit binding information
- // for this resource kind?
auto kind = typeRes.kind;
+
+ // We want to ignore resource kinds for which the user
+ // has specified an explicit binding, since those won't
+ // go into our contiguously allocated range.
+ //
auto& bindingInfo = parameterInfo->bindingInfo[(int)kind];
if( bindingInfo.count != 0 )
{
- // If things have already been bound, our work is done.
continue;
}
- auto count = typeRes.count;
-
- // We need to special-case the scenario where
- // a parameter wants to claim an entire register
- // space to itself (for a parameter block), since
- // that can't be handled like other resources.
- if (kind == LayoutResourceKind::RegisterSpace)
+ // Now we inspect the kind of resource to figure out
+ // its space requirements:
+ //
+ switch( kind )
{
- // We need to snag a register space of our own.
-
- UInt space = allocateUnusedSpaces(context, count);
+ default:
+ // An unbounded-size array will need its own space.
+ //
+ if( typeRes.count.isInfinite() )
+ {
+ spacesToAllocateCount++;
+ }
+ break;
- bindingInfo.count = count;
- bindingInfo.index = space;
+ case LayoutResourceKind::RegisterSpace:
+ // If the parameter consumes any full spaces (e.g., it
+ // is a `struct` type with one or more unbounded arrays
+ // for fields), then we will include those spaces in
+ // our allocaiton.
+ //
+ // We assume/require here that we never end up needing
+ // an unbounded number of spaces.
+ // TODO: we should enforce that somewhere with an error.
+ //
+ spacesToAllocateCount += typeRes.count.getFiniteValue();
+ break;
- // TODO: what should we store as the "space" for
- // an allocation of register spaces? Either zero
- // or `space` makes sense, but it isn't clear
- // which is a better choice.
- bindingInfo.space = 0;
+ case LayoutResourceKind::Uniform:
+ // We want to ignore uniform data for this calculation,
+ // since any uniform data in top-level shader parameters
+ // needs to go into a global constant buffer.
+ //
+ break;
- continue;
+ case LayoutResourceKind::GenericResource:
+ // This is more of a marker case, and shouldn't ever
+ // need a space allocated to it.
+ break;
}
- else if (kind == LayoutResourceKind::GenericResource)
+ }
+
+ // If we compute that the parameter needs some number of full
+ // spaces allocated to it, then we will go ahead and allocate
+ // contiguous spaces here.
+ //
+ UInt firstAllocatedSpace = 0;
+ if(spacesToAllocateCount)
+ {
+ firstAllocatedSpace = allocateUnusedSpaces(context, spacesToAllocateCount);
+ }
+
+ // We'll then dole the allocated spaces (if any) out to the resource
+ // categories that need them.
+ //
+ UInt currentAllocatedSpace = firstAllocatedSpace;
+
+ for(auto typeRes : firstTypeLayout->resourceInfos)
+ {
+ // Did we already apply some explicit binding information
+ // for this resource kind?
+ auto kind = typeRes.kind;
+ auto& bindingInfo = parameterInfo->bindingInfo[(int)kind];
+ if( bindingInfo.count != 0 )
{
- bindingInfo.space = 0;
- bindingInfo.count = 1;
- bindingInfo.index = 0;
+ // If things have already been bound, our work is done.
+ //
+ // TODO: it would be good to handle the case where a
+ // binding specified a space, but not an offset/index
+ // for some kind of resource.
+ //
continue;
}
- // Auto-generated bindings will all go in the same space,
- // which was allocated up front.
- //
- // We don't currently worry about running out of room in
- // this space; if the user declares enough parameters
- // to overflow the range then we will have other problems
- // on our hands.
- //
- // The one case that might seem like a challenge is unsized
- // arrays, since these conceptually require a (countably)
- // infinite register range.
- //
- // This turns out not to be a problem that this code
- // needs to handle, for two reasons:
- //
- // 1) In the D3D case, an unbounded-size array should be
- // computed to require one (or more) whole register spaces,
- // and so we'd end up in the `RegisterSpace` case above.
+ auto count = typeRes.count;
+
+ // Certain resource kinds require special handling.
//
- // 2) In the Vulkan case, an unbounded-size array of
- // resources still uses only a single binding, so we
- // won't run out of space.
+ // Note: This `switch` statement should have a `case` for
+ // all of the special cases above that affect the computation of
+ // `spacesToAllocateCount`.
//
- UInt space = context->shared->defaultSpace;
-
- RefPtr<UsedRangeSet> usedRangeSet;
- switch (kind)
+ switch( kind )
{
- default:
- usedRangeSet = findUsedRangeSetForSpace(context, space);
- break;
+ case LayoutResourceKind::RegisterSpace:
+ {
+ // The parameter's type needs to consume some number of whole
+ // register spaces, and we have already allocated a contiguous
+ // range of spaces above.
+ //
+ // As always, we can't handle the case of a parameter that needs
+ // an infinite number of spaces.
+ //
+ SLANG_ASSERT(count.isFinite());
+ bindingInfo.count = count;
+
+ // We will use the spaces we've allocated, and bump
+ // the variable tracking the "current" space by
+ // the number of spaces consumed.
+ //
+ bindingInfo.index = currentAllocatedSpace;
+ currentAllocatedSpace += count.getFiniteValue();
+
+ // TODO: what should we store as the "space" for
+ // an allocation of register spaces? Either zero
+ // or `space` makes sense, but it isn't clear
+ // which is a better choice.
+ bindingInfo.space = 0;
- case LayoutResourceKind::VertexInput:
- case LayoutResourceKind::FragmentOutput:
- usedRangeSet = findUsedRangeSetForTranslationUnit(context, parameterInfo->translationUnit);
+ continue;
+ }
+
+ case LayoutResourceKind::GenericResource:
+ {
+ // `GenericResource` is somewhat confusingly named,
+ // but simply indicates that the type of this parameter
+ // in some way depends on a generic parameter that has
+ // not been bound to a concrete value, so that asking
+ // specific questions about its resource usage isn't
+ // really possible.
+ //
+ bindingInfo.space = 0;
+ bindingInfo.count = 1;
+ bindingInfo.index = 0;
+ continue;
+ }
+
+ case LayoutResourceKind::Uniform:
+ // TODO: we don't currently handle global-scope uniform parameters.
break;
}
- bindingInfo.count = count;
- bindingInfo.index = usedRangeSet->usedResourceRanges[(int)kind].Allocate(parameterInfo, (int) count);
+ // At this point, we know the parameter consumes some resource
+ // (e.g., D3D `t` registers or Vulkan `binding`s), and the user
+ // didn't specify an explicit binding, so we will have to
+ // assign one for them.
+ //
+ // If we are consuming an infinite amount of the given resource
+ // (e.g., an unbounded array of `Texure2D` requires an infinite
+ // number of `t` regisers in D3D), then we will go ahead
+ // and assign a full space:
+ //
+ if( count.isInfinite() )
+ {
+ bindingInfo.count = count;
+ bindingInfo.index = 0;
+ bindingInfo.space = currentAllocatedSpace;
+ currentAllocatedSpace++;
+ }
+ else
+ {
+ // If we have a finite amount of resources, then
+ // we will go ahead and allocate from the "default"
+ // space.
- bindingInfo.space = space;
- }
+ UInt space = context->shared->defaultSpace;
- if (firstTypeLayout->FindResourceInfo(LayoutResourceKind::GenericResource))
- {
+ RefPtr<UsedRangeSet> usedRangeSet;
+ switch (kind)
+ {
+ default:
+ usedRangeSet = findUsedRangeSetForSpace(context, space);
+ break;
+
+ case LayoutResourceKind::VertexInput:
+ case LayoutResourceKind::FragmentOutput:
+ usedRangeSet = findUsedRangeSetForTranslationUnit(context, parameterInfo->translationUnit);
+ break;
+ }
+ bindingInfo.count = count;
+ bindingInfo.index = usedRangeSet->usedResourceRanges[(int)kind].Allocate(parameterInfo, count.getFiniteValue());
+
+ bindingInfo.space = space;
+ }
}
// At this point we should have explicit binding locations chosen for
@@ -2055,7 +2291,7 @@ static RefPtr<TypeLayout> processEntryPointParameter(
SLANG_RELEASE_ASSERT(rr.count != 0);
auto structRes = structLayout->findOrAddResourceInfo(rr.kind);
- fieldVarLayout->findOrAddResourceInfo(rr.kind)->index = structRes->count;
+ fieldVarLayout->findOrAddResourceInfo(rr.kind)->index = structRes->count.getFiniteValue();
structRes->count += rr.count;
}
}
@@ -2073,7 +2309,7 @@ static RefPtr<TypeLayout> processEntryPointParameter(
// so we can find the index of this generic param decl in the list
genParamTypeLayout->type = type;
genParamTypeLayout->paramIndex = findGenericParam(context->shared->programLayout->globalGenericParams, globalGenericParam.getDecl());
- genParamTypeLayout->findOrAddResourceInfo(LayoutResourceKind::GenericResource)->count++;
+ genParamTypeLayout->findOrAddResourceInfo(LayoutResourceKind::GenericResource)->count += 1;
return genParamTypeLayout;
}
else
@@ -2176,7 +2412,7 @@ static void collectEntryPointParameters(
for (auto rr : paramTypeLayout->resourceInfos)
{
auto entryPointRes = entryPointLayout->findOrAddResourceInfo(rr.kind);
- paramVarLayout->findOrAddResourceInfo(rr.kind)->index = entryPointRes->count;
+ paramVarLayout->findOrAddResourceInfo(rr.kind)->index = entryPointRes->count.getFiniteValue();
entryPointRes->count += rr.count;
}
@@ -2208,7 +2444,7 @@ static void collectEntryPointParameters(
for (auto rr : resultTypeLayout->resourceInfos)
{
auto entryPointRes = entryPointLayout->findOrAddResourceInfo(rr.kind);
- resultLayout->findOrAddResourceInfo(rr.kind)->index = entryPointRes->count;
+ resultLayout->findOrAddResourceInfo(rr.kind)->index = entryPointRes->count.getFiniteValue();
entryPointRes->count += rr.count;
}
}
@@ -2489,7 +2725,7 @@ void generateParameterBindings(
// Does the field have any uniform data?
auto layoutInfo = firstVarLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform);
- size_t uniformSize = layoutInfo ? layoutInfo->count : 0;
+ LayoutSize uniformSize = layoutInfo ? layoutInfo->count : 0;
if( uniformSize != 0 )
{
// Make sure uniform fields get laid out properly...
@@ -2498,13 +2734,13 @@ void generateParameterBindings(
uniformSize,
firstVarLayout->typeLayout->uniformAlignment);
- size_t uniformOffset = globalScopeRules->AddStructField(
+ LayoutSize uniformOffset = globalScopeRules->AddStructField(
&structLayoutInfo,
fieldInfo);
for( auto& varLayout : parameterInfo->varLayouts )
{
- varLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset;
+ varLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset.getFiniteValue();
}
}
@@ -2612,27 +2848,29 @@ RefPtr<ProgramLayout> specializeProgramLayout(
// To recover layout context, we skip generic resources in the first pass
if (varLayout->FindResourceInfo(LayoutResourceKind::GenericResource))
continue;
- SLANG_ASSERT(varLayout->resourceInfos.Count() == varLayout->typeLayout->resourceInfos.Count());
- auto uniformInfo = varLayout->FindResourceInfo(LayoutResourceKind::Uniform);
- auto tUniformInfo = varLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform);
- if (uniformInfo)
+
+ if (auto uniformInfo = varLayout->FindResourceInfo(LayoutResourceKind::Uniform))
{
anyUniforms = true;
- SLANG_ASSERT(tUniformInfo);
- structLayoutInfo.size = Math::Max(structLayoutInfo.size, uniformInfo->index + tUniformInfo->count);
+
+ if( auto tUniformInfo = varLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform) )
+ {
+ structLayoutInfo.size = maximum(structLayoutInfo.size, uniformInfo->index + tUniformInfo->count);
+ }
}
- for (UInt i = 0; i < varLayout->resourceInfos.Count(); i++)
+ for( auto resInfo : varLayout->resourceInfos )
{
- auto resInfo = varLayout->resourceInfos[i];
- auto tresInfo = varLayout->typeLayout->FindResourceInfo(resInfo.kind);
- SLANG_ASSERT(tresInfo);
- auto usedRangeSet = findUsedRangeSetForSpace(&context, resInfo.space);
- markSpaceUsed(&context, resInfo.space);
- usedRangeSet->usedResourceRanges[(int)resInfo.kind].Add(
- nullptr, // we don't need to track parameter info here
- resInfo.index,
- resInfo.index + tresInfo->count);
+ if( auto tresInfo = varLayout->typeLayout->FindResourceInfo(resInfo.kind) )
+ {
+ auto usedRangeSet = findUsedRangeSetForSpace(&context, resInfo.space);
+ markSpaceUsed(&context, resInfo.space);
+ usedRangeSet->usedResourceRanges[(int)resInfo.kind].Add(
+ nullptr, // we don't need to track parameter info here
+ resInfo.index,
+ resInfo.index + tresInfo->count);
+ }
}
+
structLayout->fields[varId] = varLayout;
varLayoutMapping[varLayout] = varLayout;
}
@@ -2658,8 +2896,8 @@ RefPtr<ProgramLayout> specializeProgramLayout(
layoutContext.with(constantBufferRules),
newType);
auto layoutInfo = newTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform);
- size_t uniformSize = layoutInfo ? layoutInfo->count : 0;
- if (uniformSize)
+ LayoutSize uniformSize = layoutInfo ? layoutInfo->count : 0;
+ if (uniformSize != 0)
{
if (globalCBufferInfo.kind == LayoutResourceKind::None)
{
@@ -2689,10 +2927,10 @@ RefPtr<ProgramLayout> specializeProgramLayout(
UniformLayoutInfo fieldInfo(
uniformSize,
newTypeLayout->uniformAlignment);
- size_t uniformOffset = layoutContext.getRulesFamily()->getConstantBufferRules()->AddStructField(
+ LayoutSize uniformOffset = layoutContext.getRulesFamily()->getConstantBufferRules()->AddStructField(
&structLayoutInfo,
fieldInfo);
- newVarLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset;
+ newVarLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset.getFiniteValue();
anyUniforms = true;
}
structLayout->fields[varId] = newVarLayout;
diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp
index 6e635a8ce..73b616377 100644
--- a/source/slang/reflection.cpp
+++ b/source/slang/reflection.cpp
@@ -513,6 +513,17 @@ SLANG_API SlangReflectionType* spReflectionTypeLayout_GetType(SlangReflectionTyp
return (SlangReflectionType*) typeLayout->type.Ptr();
}
+namespace
+{
+ static size_t getReflectionSize(LayoutSize size)
+ {
+ if(size.isFinite())
+ return size.getFiniteValue();
+
+ return SLANG_UNBOUNDED_SIZE;
+ }
+}
+
SLANG_API size_t spReflectionTypeLayout_GetSize(SlangReflectionTypeLayout* inTypeLayout, SlangParameterCategory category)
{
auto typeLayout = convert(inTypeLayout);
@@ -521,7 +532,7 @@ SLANG_API size_t spReflectionTypeLayout_GetSize(SlangReflectionTypeLayout* inTyp
auto info = typeLayout->FindResourceInfo(LayoutResourceKind(category));
if(!info) return 0;
- return info->count;
+ return getReflectionSize(info->count);
}
SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_GetFieldByIndex(SlangReflectionTypeLayout* inTypeLayout, unsigned index)
@@ -558,10 +569,10 @@ SLANG_API size_t spReflectionTypeLayout_GetElementStride(SlangReflectionTypeLayo
auto elementTypeLayout = arrayTypeLayout->elementTypeLayout;
auto info = elementTypeLayout->FindResourceInfo(LayoutResourceKind(category));
if(!info) return 0;
- return info->count;
+ return getReflectionSize(info->count);
}
- // An import special case, though, is Vulkan descriptor-table slots,
+ // An important special case, though, is Vulkan descriptor-table slots,
// where an entire array will use a single `binding`, so that the
// effective stride is zero:
case SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT:
@@ -1210,5 +1221,5 @@ SLANG_API size_t spReflection_getGlobalConstantBufferSize(SlangReflection* inPro
auto structLayout = getGlobalStructLayout(program);
auto uniform = structLayout->FindResourceInfo(LayoutResourceKind::Uniform);
if (!uniform) return 0;
- return uniform->count;
+ return getReflectionSize(uniform->count);
}
diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp
index e3b89a831..7fc6320cc 100644
--- a/source/slang/type-layout.cpp
+++ b/source/slang/type-layout.cpp
@@ -16,6 +16,15 @@ size_t RoundToAlignment(size_t offset, size_t alignment)
return offset + (alignment - remainder);
}
+LayoutSize RoundToAlignment(LayoutSize offset, size_t alignment)
+{
+ // An infinite size is assumed to be maximally aligned.
+ if(offset.isInfinite())
+ return LayoutSize::infinite();
+
+ return RoundToAlignment(offset.getFiniteValue(), alignment);
+}
+
static size_t RoundUpToPowerOfTwo( size_t value )
{
// TODO(tfoley): I know this isn't a fast approach
@@ -66,9 +75,10 @@ struct DefaultLayoutRulesImpl : SimpleLayoutRulesImpl
}
}
- SimpleArrayLayoutInfo GetArrayLayout( SimpleLayoutInfo elementInfo, size_t elementCount) override
+ SimpleArrayLayoutInfo GetArrayLayout( SimpleLayoutInfo elementInfo, LayoutSize elementCount) override
{
- size_t stride = elementInfo.size;
+ SLANG_RELEASE_ASSERT(elementInfo.size.isFinite());
+ auto stride = elementInfo.size.getFiniteValue();
SimpleArrayLayoutInfo arrayInfo;
arrayInfo.kind = elementInfo.kind;
@@ -109,7 +119,7 @@ struct DefaultLayoutRulesImpl : SimpleLayoutRulesImpl
return structInfo;
}
- size_t AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo) override
+ LayoutSize AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo) override
{
// Skip zero-size fields
if(fieldInfo.size == 0)
@@ -117,7 +127,7 @@ struct DefaultLayoutRulesImpl : SimpleLayoutRulesImpl
ioStructInfo->alignment = std::max(ioStructInfo->alignment, fieldInfo.alignment);
ioStructInfo->size = RoundToAlignment(ioStructInfo->size, fieldInfo.alignment);
- size_t fieldOffset = ioStructInfo->size;
+ LayoutSize fieldOffset = ioStructInfo->size;
ioStructInfo->size += fieldInfo.size;
return fieldOffset;
}
@@ -136,7 +146,7 @@ struct DefaultConstantBufferLayoutRulesImpl : DefaultLayoutRulesImpl
// be a multiple of 16 bytes.
//
// HLSL agrees.
- SimpleArrayLayoutInfo GetArrayLayout(SimpleLayoutInfo elementInfo, size_t elementCount) override
+ SimpleArrayLayoutInfo GetArrayLayout(SimpleLayoutInfo elementInfo, LayoutSize elementCount) override
{
if(elementInfo.kind == LayoutResourceKind::Uniform)
{
@@ -171,7 +181,9 @@ static SimpleLayoutInfo getGLSLVectorLayout(
SimpleLayoutInfo elementInfo, size_t elementCount)
{
SLANG_RELEASE_ASSERT(elementInfo.kind == LayoutResourceKind::Uniform);
- auto size = elementInfo.size * elementCount;
+ SLANG_RELEASE_ASSERT(elementInfo.size.isFinite());
+
+ auto size = elementInfo.size.getFiniteValue() * elementCount;
SimpleLayoutInfo vectorInfo(
LayoutResourceKind::Uniform,
size,
@@ -193,7 +205,7 @@ struct Std140LayoutRulesImpl : GLSLConstantBufferLayoutRulesImpl
struct HLSLConstantBufferLayoutRulesImpl : DefaultConstantBufferLayoutRulesImpl
{
// Can't let a `struct` field straddle a register (16-byte) boundary
- size_t AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo) override
+ LayoutSize AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo) override
{
// Skip zero-size fields
if(fieldInfo.size == 0)
@@ -202,8 +214,8 @@ struct HLSLConstantBufferLayoutRulesImpl : DefaultConstantBufferLayoutRulesImpl
ioStructInfo->alignment = std::max(ioStructInfo->alignment, fieldInfo.alignment);
ioStructInfo->size = RoundToAlignment(ioStructInfo->size, fieldInfo.alignment);
- size_t fieldOffset = ioStructInfo->size;
- size_t fieldSize = fieldInfo.size;
+ LayoutSize fieldOffset = ioStructInfo->size;
+ LayoutSize fieldSize = fieldInfo.size;
// Would this field cross a 16-byte boundary?
auto registerSize = 16;
@@ -690,19 +702,30 @@ TypeLayoutContext getInitialLayoutContextForTarget(TargetRequest* targetReq)
}
-static int GetElementCount(RefPtr<IntVal> val)
+static LayoutSize GetElementCount(RefPtr<IntVal> val)
{
+ // Lack of a size indicates an unbounded array.
+ if(!val)
+ return LayoutSize::infinite();
+
if (auto constantVal = val.As<ConstantIntVal>())
{
- return (int) constantVal->value;
+ return LayoutSize(LayoutSize::RawValue(constantVal->value));
}
else if( auto varRefVal = val.As<GenericParamIntVal>() )
{
- // TODO(tfoley): do something sensible in this case
+ // TODO: We want to treat the case where the number of
+ // elements in an array depends on a generic parameter
+ // much like the case where the number of elements is
+ // unbounded, *but* we can't just blindly do that because
+ // an API might disallow unbounded arrays in various
+ // cases where a generic bound might work (because
+ // any concrete specialization will have a finite bound...)
+ //
return 0;
}
SLANG_UNEXPECTED("unhandled integer literal kind");
- return 0;
+ UNREACHABLE_RETURN(LayoutSize(0));
}
bool IsResourceKind(LayoutResourceKind kind)
@@ -721,7 +744,7 @@ bool IsResourceKind(LayoutResourceKind kind)
SimpleLayoutInfo GetSimpleLayoutImpl(
SimpleLayoutInfo info,
- RefPtr<Type> type,
+ RefPtr<Type> type,
LayoutRulesImpl* rules,
RefPtr<TypeLayout>* outTypeLayout)
{
@@ -944,7 +967,10 @@ RefPtr<TypeLayout> applyOffsetToTypeLayout(
newResInfo->space = oldResInfo.space;
if (auto offsetResInfo = offsetTypeLayout->FindResourceInfo(oldResInfo.kind))
{
- newResInfo->index += offsetResInfo->count;
+ // We should not be trying to offset things by an infinite amount,
+ // since that would leave all the indices undefined.
+ SLANG_RELEASE_ASSERT(offsetResInfo->count.isFinite());
+ newResInfo->index += offsetResInfo->count.getFiniteValue();
}
}
@@ -1017,7 +1043,7 @@ createParameterGroupTypeLayout(
// Make sure that we allocate resource usage for the
// parameter block itself.
- if( parameterGroupInfo.size )
+ if( parameterGroupInfo.size != 0 )
{
containerTypeLayout->addResourceUsage(
parameterGroupInfo.kind,
@@ -1121,7 +1147,8 @@ createParameterGroupTypeLayout(
auto elementVarResInfo = elementVarLayout->findOrAddResourceInfo(kind);
if( auto containerTypeResInfo = containerTypeLayout->FindResourceInfo(kind) )
{
- elementVarResInfo->index += containerTypeResInfo->count;
+ SLANG_RELEASE_ASSERT(containerTypeResInfo->count.isFinite());
+ elementVarResInfo->index += containerTypeResInfo->count.getFiniteValue();
}
}
typeLayout->elementVarLayout = elementVarLayout;
@@ -1460,9 +1487,11 @@ bool doesResourceRequireAdjustmentForArrayOfStructs(LayoutResourceKind kind)
//
// and then we expect `foo_b` to get `register(t8)`, rather
// than `register(t1)`.
-static RefPtr<TypeLayout>maybeAdjustLayoutForArrayElementType(
+//
+static RefPtr<TypeLayout> maybeAdjustLayoutForArrayElementType(
RefPtr<TypeLayout> originalTypeLayout,
- UInt elementCount)
+ LayoutSize elementCount,
+ UInt& ioAdditionalSpacesNeeded)
{
// We will start by looking for cases that we can reject out
// of hand.
@@ -1494,7 +1523,8 @@ static RefPtr<TypeLayout>maybeAdjustLayoutForArrayElementType(
auto originalInnerElementTypeLayout = originalArrayTypeLayout->elementTypeLayout;
auto adjustedInnerElementTypeLayout = maybeAdjustLayoutForArrayElementType(
originalInnerElementTypeLayout,
- elementCount);
+ elementCount,
+ ioAdditionalSpacesNeeded);
// If nothing needed to be changed on the inner element type,
// then we are done.
@@ -1516,7 +1546,8 @@ static RefPtr<TypeLayout>maybeAdjustLayoutForArrayElementType(
auto originalInnerElementTypeLayout = originalParameterGroupTypeLayout->elementVarLayout->typeLayout;
auto adjustedInnerElementTypeLayout = maybeAdjustLayoutForArrayElementType(
originalInnerElementTypeLayout,
- elementCount);
+ elementCount,
+ ioAdditionalSpacesNeeded);
// If nothing needed to be changed on the inner element type,
// then we are done.
@@ -1537,20 +1568,37 @@ static RefPtr<TypeLayout>maybeAdjustLayoutForArrayElementType(
if(fieldCount == 0)
return originalTypeLayout;
- // TODO: we could try to special-case a `struct` type with a single
- // field that needs no adjustment, just to avoid some extra allocation.
-
RefPtr<StructTypeLayout> adjustedStructTypeLayout = new StructTypeLayout();
copyTypeLayoutFields(adjustedStructTypeLayout, originalStructTypeLayout);
+ // If the array type adjustment forces us to give a whole space to
+ // one or more fields, then we'll need to carefully compute the space
+ // index for each field as we go.
+ //
+ LayoutSize nextSpaceIndex = 0;
+
Dictionary<RefPtr<VarLayout>, RefPtr<VarLayout>> mapOriginalFieldToAdjusted;
for( auto originalField : originalStructTypeLayout->fields )
{
- // Compute the adjusted type for the field
auto originalFieldTypeLayout = originalField->typeLayout;
+
+ LayoutSize originalFieldSpaceCount = 0;
+ if(auto resInfo = originalFieldTypeLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace))
+ originalFieldSpaceCount = resInfo->count;
+
+ // Compute the adjusted type for the field
+ UInt fieldAdditionalSpaces = 0;
auto adjustedFieldTypeLayout = maybeAdjustLayoutForArrayElementType(
originalFieldTypeLayout,
- elementCount);
+ elementCount,
+ fieldAdditionalSpaces);
+
+ LayoutSize adjustedFieldSpaceCount = originalFieldSpaceCount + fieldAdditionalSpaces;
+
+ LayoutSize spaceOffsetForField = nextSpaceIndex;
+ nextSpaceIndex += adjustedFieldSpaceCount;
+
+ ioAdditionalSpacesNeeded += fieldAdditionalSpaces;
// Create an adjusted field variable, that is mostly
// a clone of the original field (just with our
@@ -1559,16 +1607,31 @@ static RefPtr<TypeLayout>maybeAdjustLayoutForArrayElementType(
copyVarLayoutFields(adjustedField, originalField);
adjustedField->typeLayout = adjustedFieldTypeLayout;
- // Finally we get down to the real meat of the change,
- // which is that the field offsets for any resource-type
- // fields need to be "adjusted" which amounts to just
- // multiplying them by the element count of the array.
-
- for( auto& resInfo : adjustedField->resourceInfos )
+ // We will now walk through the resource usage for
+ // the adjusted field, and try to figure out what
+ // to do with it all.
+ //
+ for(auto& resInfo : adjustedField->resourceInfos )
{
if( doesResourceRequireAdjustmentForArrayOfStructs(resInfo.kind) )
{
- resInfo.index *= elementCount;
+ if(elementCount.isFinite())
+ {
+ // If the array size is finite, then the field's index/offset
+ // is just going to be strided by the array size since we
+ // are effectively doing AoS to SoA conversion.
+ //
+ resInfo.index *= elementCount.getFiniteValue();
+ }
+ else
+ {
+ // If we are making an unbounded array, then a `struct`
+ // field with resource type will turn into its own space,
+ // and it will start at regsiter zero in that space.
+ //
+ resInfo.index = 0;
+ resInfo.space = spaceOffsetForField.getFiniteValue();
+ }
}
}
@@ -1592,8 +1655,16 @@ static RefPtr<TypeLayout>maybeAdjustLayoutForArrayElementType(
}
else
{
- // If the leaf type layout isn't some kind of aggregate,
- // then we can just bail out here.
+ // In the leaf case, we must have a field that used up some resource
+ // that requires adjustment. Because there is no sub-structure to work
+ // with, we can just return the type layout as-is, but we also want
+ // to make a note that this value should consume an additional register
+ // space *if* the element count is unbounded.
+ if( elementCount.isInfinite() )
+ {
+ ioAdditionalSpacesNeeded++;
+ }
+
return originalTypeLayout;
}
}
@@ -1822,12 +1893,27 @@ SimpleLayoutInfo GetLayoutImpl(
arrayType->baseType.Ptr(),
outTypeLayout ? &elementTypeLayout : nullptr);
- // For layout purposes, we treat an unsized array as an array of zero elements.
+ // To a first approximation, an array will usually be laid out
+ // by taking the element's type layout and laying out `elementCount`
+ // copies of it. There are of course many details that make
+ // this simplistic version of things not quite work.
+ //
+ // An important complication to deal with is the possibility of
+ // having "unbounded" arrays, which don't specify a size.'
+ // The layout rules for these vary heavily by resource kind and API.
//
- // TODO: Longer term we are going to need to be careful to include some indication
- // that a type has logically "infinite" size in some resource kind. In particular
- // this affects how we would allocate space for parameter binding purposes.
- auto elementCount = arrayType->ArrayLength ? GetElementCount(arrayType->ArrayLength) : 0;
+
+ auto elementCount = GetElementCount(arrayType->ArrayLength);
+
+ //
+ // We can compute the uniform storage layout of an array using
+ // the rules for the target API.
+ //
+ // TODO: ensure that this does something reasonable with the unbounded
+ // case, or else issue an error message that the target doesn't
+ // support unbounded types.
+ //
+
auto arrayUniformInfo = rules->GetArrayLayout(
elementInfo,
elementCount).getUniformLayout();
@@ -1837,54 +1923,117 @@ SimpleLayoutInfo GetLayoutImpl(
RefPtr<ArrayTypeLayout> typeLayout = new ArrayTypeLayout();
*outTypeLayout = typeLayout;
- // If we construct an array over an aggregate type that contains
- // resource fields, we may need to adjust the layout we create
- // for the element type to
- RefPtr<TypeLayout> adjustedElementTypeLayout = maybeAdjustLayoutForArrayElementType(
- elementTypeLayout,
- elementCount);
-
+ // Some parts of the array type layout object are easy to fill in:
typeLayout->type = type;
- typeLayout->originalElementTypeLayout = elementTypeLayout;
- typeLayout->elementTypeLayout = adjustedElementTypeLayout;
typeLayout->rules = rules;
-
+ typeLayout->originalElementTypeLayout = elementTypeLayout;
typeLayout->uniformAlignment = arrayUniformInfo.alignment;
typeLayout->uniformStride = arrayUniformInfo.elementStride;
typeLayout->addResourceUsage(LayoutResourceKind::Uniform, arrayUniformInfo.size);
- // translate element-type resources into array-type resources
+ //
+ // The tricky part in constructing an array type layout comes when
+ // the element type is (or nests) a structure with resource-type
+ // fields, because in that case we need to perform AoS-to-SoA
+ // conversion as part of computing the final type layout, and
+ // we also need to pre-compute an "adjusted" element type
+ // layout that accounts for the striding that happens with
+ // resource-type contents.
+ //
+ // This complication is only made worse when we have to deal with
+ // unbounded-size arrays over such element types, since those
+ // resource-type fields will each end up consuming a full space
+ // in the resulting layout.
+ //
+ // The `maybeAdjustLayoutForArrayElementType` computes an "adjusted"
+ // type layout for the element type which takes the array stride into
+ // acount. If it returns the same type layout that was passed in,
+ // then that means no adjustement took place.
+ //
+ // The `additionalSpacesNeededForAdjustedElementType` variable counts
+ // the number of additional register spaces that were consumed,
+ // in the case of an unbounded array.
+ //
+ UInt additionalSpacesNeededForAdjustedElementType = 0;
+ RefPtr<TypeLayout> adjustedElementTypeLayout = maybeAdjustLayoutForArrayElementType(
+ elementTypeLayout,
+ elementCount,
+ additionalSpacesNeededForAdjustedElementType);
+
+ typeLayout->elementTypeLayout = adjustedElementTypeLayout;
+
+ // We will now iterate over the resources consumed by the element
+ // type to compute how they contribute to the resource usage
+ // of the overall array type.
+ //
for( auto elementResourceInfo : elementTypeLayout->resourceInfos )
{
// The uniform case was already handled above
if( elementResourceInfo.kind == LayoutResourceKind::Uniform )
continue;
+ LayoutSize arrayResourceCount = 0;
+
// In almost all cases, the resources consumed by an array
// will be its element count times the resources consumed
- // by its element type. The one exception to this is
- // arrays of resources in Vulkan GLSL, where an entire array
+ // by its element type.
+ //
+ // The first exception to this is arrays of resources when
+ // compiling to GLSL for Vulkan, where an entire array
// only consumes a single descriptor-table slot.
//
- // Note: We extend this logic to arbitrary arrays-of-structs,
- // under the assumption that downstream legalization will
- // turn those into scalarized structs-of-arrays and this
- // logic will work out.
- UInt arrayResourceCount = 0;
if (elementResourceInfo.kind == LayoutResourceKind::DescriptorTableSlot)
{
arrayResourceCount = elementResourceInfo.count;
}
+ //
+ // The next big exception is when we are forming an unbounded-size
+ // array and the element type got "adjusted," because that means
+ // the array type will need to allocate full spaces for any resource-type
+ // fields in the element type.
+ //
+ // Note: we carefully carve things out so that the case of a simple
+ // array of resources does *not* lead to the element type being adjusted,
+ // so that this logic doesn't trigger and we instead handle it with
+ // the default logic below.
+ //
+ else if(
+ elementCount.isInfinite()
+ && adjustedElementTypeLayout != elementTypeLayout
+ && doesResourceRequireAdjustmentForArrayOfStructs(elementResourceInfo.kind) )
+ {
+ // We want to ignore resource types consumed by the element type
+ // that need adjustement if the array size is infinite, since
+ // we will be allocating whole spaces for that part of the
+ // element's resource usage.
+ }
else
{
arrayResourceCount = elementResourceInfo.count * elementCount;
}
-
+
+ // Now that we've computed how the resource usage of the element type
+ // should contribute to the resource usage of the array, we can
+ // add in that resource usage.
+ //
typeLayout->addResourceUsage(
elementResourceInfo.kind,
arrayResourceCount);
}
+
+ // The loop above to compute the resource usage of the array from its
+ // element type ignored any resource-type fields in an unbounded-size
+ // array if they would have been allocated as full register spaces.
+ // Those same fields were counted in `additionalSpacesNeededForAdjustedElementType`,
+ // and need to be added into the total resource usage for the array
+ // if we skipped them as part of the loop (which happens when
+ // we detect that the element type layout had been "adjusted").
+ //
+ if( adjustedElementTypeLayout != elementTypeLayout )
+ {
+ typeLayout->addResourceUsage(LayoutResourceKind::RegisterSpace, additionalSpacesNeededForAdjustedElementType);
+ }
}
return arrayUniformInfo;
}
@@ -1903,6 +2052,14 @@ SimpleLayoutInfo GetLayoutImpl(
*outTypeLayout = typeLayout;
}
+ // The layout of a `struct` type is computed in the somewhat
+ // obvious fashion by keeping a running counter of the resource
+ // usage for each kind of resource, and then for a field that
+ // uses a given resource, assigning it the current offset and
+ // then bumping the offset by the field size. In the case of
+ // uniform data we also need to deal with alignment and other
+ // detailed layout rules.
+
UniformLayoutInfo info = rules->BeginStructLayout();
for (auto field : GetFields(structDeclRef))
@@ -1922,7 +2079,7 @@ SimpleLayoutInfo GetLayoutImpl(
// This means that the code to generate final
// declarations needs to *also* eliminate zero-size
// fields to be safe...
- size_t uniformOffset = info.size;
+ LayoutSize uniformOffset = info.size;
if(fieldInfo.size != 0)
{
uniformOffset = rules->AddStructField(&info, fieldInfo);
@@ -1942,7 +2099,7 @@ SimpleLayoutInfo GetLayoutImpl(
// Set up uniform offset information, if there is any uniform data in the field
if( fieldTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform) )
{
- fieldLayout->AddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset;
+ fieldLayout->AddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset.getFiniteValue();
}
// Add offset information for any other resource kinds
@@ -1958,10 +2115,39 @@ SimpleLayoutInfo GetLayoutImpl(
// The field will need offset information for this kind
auto fieldResourceInfo = fieldLayout->AddResourceInfo(fieldTypeResourceInfo.kind);
- // Check how many slots of the given kind have already been added to the type
- auto structTypeResourceInfo = typeLayout->findOrAddResourceInfo(fieldTypeResourceInfo.kind);
- fieldResourceInfo->index = structTypeResourceInfo->count;
- structTypeResourceInfo->count += fieldTypeResourceInfo.count;
+ // It is possible for a `struct` field to use an unbounded array
+ // type, and in the D3D case that would consume an unbounded number
+ // of registers. What is more, a single `struct` could have multiple
+ // such fields, or ordinary resource fields after an unbounded field.
+ //
+ // We handle this case by allocating a distinct register space for
+ // any field that consumes an unbounded amount of registers.
+ //
+ if( fieldTypeResourceInfo.count.isInfinite() )
+ {
+ // We need to add one register space to own the storage for this field.
+ //
+ auto structTypeSpaceResourceInfo = typeLayout->findOrAddResourceInfo(LayoutResourceKind::RegisterSpace);
+ auto spaceOffset = structTypeSpaceResourceInfo->count;
+ structTypeSpaceResourceInfo->count += 1;
+
+ // The field itself will record itself as having a zero offset into
+ // the chosen space.
+ //
+ fieldResourceInfo->space = spaceOffset.getFiniteValue();
+ fieldResourceInfo->index = 0;
+ }
+ else
+ {
+ // In the case where the field consumes a finite number of slots, we
+ // can simply set its offset/index to the number of such slots consumed
+ // so far, and then increment the number of slots consumed by the
+ // `struct` type itself.
+ //
+ auto structTypeResourceInfo = typeLayout->findOrAddResourceInfo(fieldTypeResourceInfo.kind);
+ fieldResourceInfo->index = structTypeResourceInfo->count.getFiniteValue();
+ structTypeResourceInfo->count += fieldTypeResourceInfo.count;
+ }
}
}
}
@@ -1989,7 +2175,7 @@ SimpleLayoutInfo GetLayoutImpl(
genParamTypeLayout->type = type;
genParamTypeLayout->paramIndex = findGenericParam(context.targetReq->layout->globalGenericParams, genParamTypeLayout->getGlobalGenericParamDecl());
genParamTypeLayout->rules = rules;
- genParamTypeLayout->findOrAddResourceInfo(LayoutResourceKind::GenericResource)->count++;
+ genParamTypeLayout->findOrAddResourceInfo(LayoutResourceKind::GenericResource)->count += 1;
*outTypeLayout = genParamTypeLayout;
}
return info;
diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h
index 7f662bdee..32ee41784 100644
--- a/source/slang/type-layout.h
+++ b/source/slang/type-layout.h
@@ -36,12 +36,152 @@ enum class LayoutRulesFamily
};
#endif
+// A "size" that can either be a simple finite size or
+// the special case of an infinite/unbounded size.
+//
+struct LayoutSize
+{
+ typedef size_t RawValue;
+
+ LayoutSize()
+ : raw(0)
+ {}
+
+ LayoutSize(RawValue size)
+ : raw(size)
+ {
+ SLANG_ASSERT(size != RawValue(-1));
+ }
+
+ static LayoutSize infinite()
+ {
+ LayoutSize result;
+ result.raw = RawValue(-1);
+ return result;
+ }
+
+ bool isInfinite() const { return raw == RawValue(-1); }
+
+ bool isFinite() const { return raw != RawValue(-1); }
+ RawValue getFiniteValue() const { SLANG_ASSERT(isFinite()); return raw; }
+
+ bool operator==(LayoutSize that) const
+ {
+ return raw == that.raw;
+ }
+
+ bool operator!=(LayoutSize that) const
+ {
+ return raw != that.raw;
+ }
+
+ void operator+=(LayoutSize right)
+ {
+ if( isInfinite() ) {}
+ else if( right.isInfinite() )
+ {
+ *this = LayoutSize::infinite();
+ }
+ else
+ {
+ *this = LayoutSize(raw + right.raw);
+ }
+ }
+
+ void operator*=(LayoutSize right)
+ {
+ // Deal with zero first, so that anything (even the "infinite" value) times zero is zero.
+ if( raw == 0 )
+ {
+ return;
+ }
+
+ if( right.raw == 0 )
+ {
+ raw = 0;
+ return;
+ }
+
+ // Next we deal with infinite cases, so that infinite times anything non-zero is infinite
+ if( isInfinite() )
+ {
+ return;
+ }
+
+ if( right.isInfinite() )
+ {
+ *this = LayoutSize::infinite();
+ return;
+ }
+
+ // Finally deal with the case where both sides are finite
+ *this = LayoutSize(raw * right.raw);
+ }
+
+ void operator-=(RawValue right)
+ {
+ if( isInfinite() ) {}
+ else
+ {
+ *this = LayoutSize(raw - right);
+ }
+ }
+
+ void operator/=(RawValue right)
+ {
+ if( isInfinite() ) {}
+ else
+ {
+ *this = LayoutSize(raw / right);
+ }
+ }
+ RawValue raw;
+};
+
+inline LayoutSize operator+(LayoutSize left, LayoutSize right)
+{
+ LayoutSize result(left);
+ result += right;
+ return result;
+}
+
+inline LayoutSize operator*(LayoutSize left, LayoutSize right)
+{
+ LayoutSize result(left);
+ result *= right;
+ return result;
+}
+
+inline LayoutSize operator-(LayoutSize left, LayoutSize::RawValue right)
+{
+ LayoutSize result(left);
+ result -= right;
+ return result;
+}
+
+inline LayoutSize operator/(LayoutSize left, LayoutSize::RawValue right)
+{
+ LayoutSize result(left);
+ result /= right;
+ return result;
+}
+
+inline LayoutSize maximum(LayoutSize left, LayoutSize right)
+{
+ if(left.isInfinite() || right.isInfinite())
+ return LayoutSize::infinite();
+
+ return LayoutSize(Math::Max(
+ left.getFiniteValue(),
+ right.getFiniteValue()));
+}
+
// Layout appropriate to "just memory" scenarios,
// such as laying out the members of a constant buffer.
struct UniformLayoutInfo
{
- size_t size;
- size_t alignment;
+ LayoutSize size;
+ size_t alignment;
UniformLayoutInfo()
: size(0)
@@ -49,8 +189,8 @@ struct UniformLayoutInfo
{}
UniformLayoutInfo(
- size_t size,
- size_t alignment)
+ LayoutSize size,
+ size_t alignment)
: size(size)
, alignment(alignment)
{}
@@ -68,9 +208,9 @@ struct UniformArrayLayoutInfo : UniformLayoutInfo
{}
UniformArrayLayoutInfo(
- size_t size,
- size_t alignment,
- size_t elementStride)
+ LayoutSize size,
+ size_t alignment,
+ size_t elementStride)
: UniformLayoutInfo(size, alignment)
, elementStride(elementStride)
{}
@@ -86,7 +226,7 @@ struct SimpleLayoutInfo
LayoutResourceKind kind;
// How many resources of that kind?
- size_t size;
+ LayoutSize size;
// only useful in the uniform case
size_t alignment;
@@ -104,7 +244,7 @@ struct SimpleLayoutInfo
, alignment(uniformInfo.alignment)
{}
- SimpleLayoutInfo(LayoutResourceKind kind, size_t size, size_t alignment=1)
+ SimpleLayoutInfo(LayoutResourceKind kind, LayoutSize size, size_t alignment=1)
: kind(kind)
, size(size)
, alignment(alignment)
@@ -168,7 +308,7 @@ public:
LayoutResourceKind kind = LayoutResourceKind::None;
// How many registers of the above kind did we use?
- UInt count;
+ LayoutSize count;
};
List<ResourceInfo> resourceInfos;
@@ -207,7 +347,7 @@ public:
findOrAddResourceInfo(info.kind)->count += info.count;
}
- void addResourceUsage(LayoutResourceKind kind, UInt count)
+ void addResourceUsage(LayoutResourceKind kind, LayoutSize count)
{
ResourceInfo info;
info.kind = kind;
@@ -507,7 +647,7 @@ struct SimpleLayoutRulesImpl
virtual SimpleLayoutInfo GetScalarLayout(BaseType baseType) = 0;
// Get size and alignment for an array of elements
- virtual SimpleArrayLayoutInfo GetArrayLayout(SimpleLayoutInfo elementInfo, size_t elementCount) = 0;
+ virtual SimpleArrayLayoutInfo GetArrayLayout(SimpleLayoutInfo elementInfo, LayoutSize elementCount) = 0;
// Get layout for a vector or matrix type
virtual SimpleLayoutInfo GetVectorLayout(SimpleLayoutInfo elementInfo, size_t elementCount) = 0;
@@ -517,7 +657,7 @@ struct SimpleLayoutRulesImpl
virtual UniformLayoutInfo BeginStructLayout() = 0;
// Add a field to a `struct` type, and return the offset for the field
- virtual size_t AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo) = 0;
+ virtual LayoutSize AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo) = 0;
// End layout for a struct, and finalize its size/alignment.
virtual void EndStructLayout(UniformLayoutInfo* ioStructInfo) = 0;
@@ -542,7 +682,7 @@ struct LayoutRulesImpl
return simpleRules->GetScalarLayout(baseType);
}
- SimpleArrayLayoutInfo GetArrayLayout(SimpleLayoutInfo elementInfo, size_t elementCount)
+ SimpleArrayLayoutInfo GetArrayLayout(SimpleLayoutInfo elementInfo, LayoutSize elementCount)
{
return simpleRules->GetArrayLayout(elementInfo, elementCount);
}
@@ -562,7 +702,7 @@ struct LayoutRulesImpl
return simpleRules->BeginStructLayout();
}
- size_t AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo)
+ LayoutSize AddStructField(UniformLayoutInfo* ioStructInfo, UniformLayoutInfo fieldInfo)
{
return simpleRules->AddStructField(ioStructInfo, fieldInfo);
}