diff options
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/compiler.cpp | 13 | ||||
| -rw-r--r-- | source/slang/parameter-binding.cpp | 462 | ||||
| -rw-r--r-- | source/slang/reflection.cpp | 19 | ||||
| -rw-r--r-- | source/slang/type-layout.cpp | 318 | ||||
| -rw-r--r-- | source/slang/type-layout.h | 170 |
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); } |
