diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2018-11-21 08:15:33 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-11-21 08:15:33 -0800 |
| commit | 9bb11b69a08c66e2857f439837e2253658aed9a4 (patch) | |
| tree | dd089ff2e60c3074e4a65422acb083c6bf94e67e /source/slang/type-layout.cpp | |
| parent | 7f97f35ee51f829e0b33a2916e651d727a5c51fa (diff) | |
Add support for unbounded arrays as shader parameters (#725)
* Add support for unbounded arrays as shader parameters
With this change, Slang shaders can use unbounded-size arrays as parameters, e.g.:
```hlsl
Texture2D t[] : register(t3, space2);
SamplerState s[];
```
As shown in the above example, Slang supports both explicit `register` declarations on unbounded-size arrays and also implicit binding.
When doing automatic parmaeter binding, Slang will allocate a full register space to an unbounded-size array of textures/smaplers, starting at register zero.
Note that for the Vulkan target, an array of descriptors of any size (including unbounded size) consumes only a single `bindign`, so much of this logic is specific to D3D targets.
Details on the changes made:
* The single biggest change is a new `LayoutSize` type that is used to store a value that can either be a finite unsigned integer or a dedicated "infinite" value (which is stored as the all-bits-set `-1` value). This is used in places where a size could either be a finite value or an "unbounded" value, to both try to make standard math robust against the infinite case, and also to force code to deal with both the finite and infinite cases more explicitly when they care about the difference.
* The public API was documented so that unbounded-size arrays report their size as `-1`. We should probably change this function to return a signed value instead of `size_t`, but that would technically be a source-breaking change, so we want to make sure we stage it appropriately.
* The code that invokes fxc was updated so that it passes the appropriate flag to enable unbounded arrays of descriptors. I haven't looked yet at whether dxc needs such a flag, so there may need to be a follow-on change to add that.
* The logic in the `UsedRanges::Add` method for tracking what registers have been claimed was rewritten because the previous version had some subtle bugs. The new version includes more detailed comments that attempt to explain why I think the new logic works.
* The top-level logic for auto-assigning bindings to parameters has been overhauled to deal with the fact that a parameter that needs "infinite" amounts of a resource should be claiming a full register space for those resources instead. Whenever a parameter allocates any register spaces we want them all to be contiguous, so we have a loop that counts the requirements and allocates the spaces before we go along and dole them out.
* When computing the layout for an array type, we need to carefully deal with unbounded-size arrays. In the case of an unbounded array of a "simple" resource type (e.g., `Texture2D[]`), we opt to expose the type layout as consuming an infinite number of the appropriate register, while in the case of a complex type (say, a `struct` with two texture fields), we need to instead allocate whole spaces for those fields. The logic here is more subtle than I would like, and interacts with the existing code that "adjusts" the element type of an array in order to make standard indexing math Just Work.
* Similarly, when a `struct` type has unbounded-array fields, then we need to transform any field with infinite register requirements to instead consume a space in the resulting aggregate type. This case is comparatively easier than the array case.
* The test case for unbounded arrays covers both explicit and implicit bindings, and also the case of an unbounded array over a `struct` type (it does not cover the case of a `struct` contianing unbounded arrays, so that will need to be added later). For this test we are both validation the output reflection data and that we produce the same code as fxc (with explicit bindings in the fxc case).
* The reflection test app was modified to use the new API contract and detect when a parameter consumes `SLANG_UNBOUNDED_SIZE` resources.
* Fixup: ensure unbounded size is defined at right bit width
Diffstat (limited to 'source/slang/type-layout.cpp')
| -rw-r--r-- | source/slang/type-layout.cpp | 318 |
1 files changed, 252 insertions, 66 deletions
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; |
