diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2019-02-27 14:58:02 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-02-27 14:58:02 -0800 |
| commit | 15bab62e69286a835b68e3c3aab6ba6c946f3715 (patch) | |
| tree | 911903f12984e5195099eb187f318c06ea9b2dd6 | |
| parent | 413aa5a0f9d92bfaecf4b8c3fad357812847695d (diff) | |
Simplify type layout (#867)
* Make vector/matrix type layouts include element type layouts
Previously the `MatrixTypeLayout` class was a leaf node in the layout hierarchy, and vector types just used `TypeLayout` with no further refinement.
This change adds a `VectorTypeLayout`, and makes all of vector, matrix, and array types inherit form a common base class for `SequenceTypeLayout`s.
The actual layout computation logic was updated to compute layouts for the element types of vectors, and for the row (and element) types of matrices.
Notes:
* Because of the way varying input/output parameters are being handled, their type layouts won't include this new information, and they will just use `TypeLayout`. This was true even for the matrix case before.
* I made the design choice in this change to have a matrix type always treat rows as the logical element type (since that is what is surfaced to ther user in the HLSL syntax). We could potentially make our lives easier during layout computation if we made the element type of a `MatrixTypeLayout` depend on the row-/column-major layout choice, but that would then require any algorithm that uses the new layout information to take row-vs-column-major into account.
* No code is actually *using* this new information yet, although the work in `ir-union.cpp` could probably benefit from it. The main expected use case going forward is representing constant buffers as a "bag of bits."
* Add a specialized type layout approach for varying parameters
There is a lot of complexity in `GetLayoutImpl` because it needs to service both the "normal" case, which always wants a `TypeLayout` object to be returned, and the varying parameter case, where we currently don't care about getting back a `TypeLayout` object.
Confusingly, the varying parameter layout logic actually *does* construct `TypeLayout` objects, and it just does it inside of `parameter-binding.cpp` rather than in `type-layout.cpp`. That logic cannot (easily) be shared with the `GetLayoutImpl` path because:
* The varying case needs to deal with system-value semantics and also parameters that may be inputs, outputs, or both (so that they need to combine resource usage computed for inputs and outputs).
* The varying case needs to special-case vectors (and to a lesser extent matrices) because of the quirks of uniform layout (e.g., four `float` varying inputs consume four `locations`, but a `float4` consumes only one location).
This change introduces a customized layout function just for varying parameters, that only handles the scalar, vector, and matrix cases (since `parameter-binding.cpp` will have recursed through any strucures/arrays, and should error out on any other types that are illegal in varying parameter lists).
In the long run we could consider trying to deduplicate code and share more of this logic with `GetLayoutImpl`, but that would require a more significant refactoring of type layout, which should probably wait until we are doing layout on IR types.
* Rename CreateTypeLayout to createTypeLayout
This is just a fixup to better reflect our established naming conventions.
* Simplify type layout so that it always returns a layout object
The core `GetLayoutImpl` routine included a fair bit of complexity to deal with the fact that its `outTypeLayout` parameter was optional.
The caller could pass in null to say that it doesn't want a `TypeLayout` object to be constructed, and the routine would conditionalize a lot of its logic to deal with this case.
This change simplifies the logic so that a `TypeLayout` is always constructed and returned. Instead of using a combination of a function result (for the `SimpleLayoutInfo`) and an output parameter (for the `TypeLayout`) we use a new `TypeLayoutResult` that acts as a tuple over the two.
I had hoped for a more significant cleanup by also eliminating the need to return the `SimpleLayoutInfo` separately from the `TypeLayout`, but the simple layout info is what the underlying per-API/-context "rules" implementations use (so that they can avoid all the complexity of `TypeLayout`), and refactoring to derive the simple layout infor from a computed `TypeLayout` would be a bigger refactoring than I was ready for.
* fixup: typos
| -rw-r--r-- | source/slang/parameter-binding.cpp | 76 | ||||
| -rw-r--r-- | source/slang/reflection.cpp | 2 | ||||
| -rw-r--r-- | source/slang/type-layout.cpp | 913 | ||||
| -rw-r--r-- | source/slang/type-layout.h | 111 |
4 files changed, 642 insertions, 460 deletions
diff --git a/source/slang/parameter-binding.cpp b/source/slang/parameter-binding.cpp index 56c5d7c1d..c56ace9fe 100644 --- a/source/slang/parameter-binding.cpp +++ b/source/slang/parameter-binding.cpp @@ -1117,7 +1117,7 @@ RefPtr<TypeLayout> getTypeLayoutForGlobalShaderParameter( if( varDecl->HasModifier<ShaderRecordAttribute>() && as<ConstantBufferType>(type) ) { - return CreateTypeLayout( + return createTypeLayout( layoutContext.with(rules->getShaderRecordConstantBufferRules()), type); } @@ -1127,7 +1127,7 @@ RefPtr<TypeLayout> getTypeLayoutForGlobalShaderParameter( // qualifier before we move on to anything else. if (varDecl->HasModifier<PushConstantAttribute>() && as<ConstantBufferType>(type)) { - return CreateTypeLayout( + return createTypeLayout( layoutContext.with(rules->getPushConstantBufferRules()), type); } @@ -1144,7 +1144,7 @@ RefPtr<TypeLayout> getTypeLayoutForGlobalShaderParameter( // An "ordinary" global variable is implicitly a uniform // shader parameter. - return CreateTypeLayout( + return createTypeLayout( layoutContext.with(rules->getConstantBufferRules()), type); } @@ -1158,13 +1158,6 @@ RefPtr<TypeLayout> getTypeLayoutForGlobalShaderParameter( // -enum EntryPointParameterDirection -{ - kEntryPointParameterDirection_Input = 0x1, - kEntryPointParameterDirection_Output = 0x2, -}; -typedef unsigned int EntryPointParameterDirectionMask; - struct EntryPointParameterState { String* optSemanticName = nullptr; @@ -1891,7 +1884,7 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter( String semanticName = optSemanticName ? *optSemanticName : ""; String sn = semanticName.ToLower(); - RefPtr<TypeLayout> typeLayout = new TypeLayout(); + RefPtr<TypeLayout> typeLayout; if (sn.StartsWith("sv_") || sn.StartsWith("nv_")) { @@ -1915,11 +1908,11 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter( // We also need to track this as an ordinary varying output from the stage, // since that is how GLSL will want to see it. - auto rules = context->getRulesFamily()->getVaryingOutputRules(); - SimpleLayoutInfo layout = GetLayout( - context->layoutContext.with(rules), - type); - typeLayout->addResourceUsage(layout.kind, layout.size); + // + typeLayout = getSimpleVaryingParameterTypeLayout( + context->layoutContext, + type, + kEntryPointParameterDirection_Output); } } @@ -1931,6 +1924,21 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter( } } + if( !typeLayout ) + { + // If we didn't compute a special-case layout for the + // system-value parameter (e.g., because it was an + // `SV_Target` output), then create a default layout + // that consumes no input/output varying slots. + // (since system parameters are distinct from + // user-defined parameters for layout purposes) + // + typeLayout = getSimpleVaryingParameterTypeLayout( + context->layoutContext, + type, + 0); + } + // Remember the system-value semantic so that we can query it later if (varLayout) { @@ -1942,25 +1950,13 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter( } else { - // user-defined semantic - - if (state.directionMask & kEntryPointParameterDirection_Input) - { - auto rules = context->getRulesFamily()->getVaryingInputRules(); - SimpleLayoutInfo layout = GetLayout( - context->layoutContext.with(rules), - type); - typeLayout->addResourceUsage(layout.kind, layout.size); - } - - if (state.directionMask & kEntryPointParameterDirection_Output) - { - auto rules = context->getRulesFamily()->getVaryingOutputRules(); - SimpleLayoutInfo layout = GetLayout( - context->layoutContext.with(rules), - type); - typeLayout->addResourceUsage(layout.kind, layout.size); - } + // In this case we have a user-defined semantic, which means + // an ordinary input and/or output varying parameter. + // + typeLayout = getSimpleVaryingParameterTypeLayout( + context->layoutContext, + type, + state.directionMask); } if (state.isSampleRate @@ -2097,13 +2093,13 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter( case Stage::ClosestHit: case Stage::Miss: // `in out` or `out` parameter is payload - return CreateTypeLayout(context->layoutContext.with( + return createTypeLayout(context->layoutContext.with( context->getRulesFamily()->getRayPayloadParameterRules()), type); case Stage::Callable: // `in out` or `out` parameter is payload - return CreateTypeLayout(context->layoutContext.with( + return createTypeLayout(context->layoutContext.with( context->getRulesFamily()->getCallablePayloadParameterRules()), type); @@ -2133,7 +2129,7 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter( case Stage::AnyHit: case Stage::ClosestHit: // `in` parameter is hit attributes - return CreateTypeLayout(context->layoutContext.with( + return createTypeLayout(context->layoutContext.with( context->getRulesFamily()->getHitAttributesParameterRules()), type); } @@ -2294,7 +2290,7 @@ static RefPtr<TypeLayout> computeEntryPointParameterTypeLayout( // a uniform shader parameter passed via the implicitly-defined // constant buffer (e.g., the `$Params` constant buffer seen in fxc/dxc output). // - return CreateTypeLayout( + return createTypeLayout( context->layoutContext.with(context->getRulesFamily()->getConstantBufferRules()), paramType); } @@ -2518,7 +2514,7 @@ static void collectEntryPointParameters( { SLANG_ASSERT(taggedUnionType); auto substType = taggedUnionType->Substitute(typeSubst).as<Type>(); - auto typeLayout = CreateTypeLayout(context->layoutContext, substType); + auto typeLayout = createTypeLayout(context->layoutContext, substType); entryPointLayout->taggedUnionTypeLayouts.Add(typeLayout); } diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp index b40900faf..f9c704437 100644 --- a/source/slang/reflection.cpp +++ b/source/slang/reflection.cpp @@ -609,7 +609,7 @@ SLANG_API SlangReflectionTypeLayout* spReflection_GetTypeLayout( RefPtr<TypeLayout> result; if (targetReq->getTypeLayouts().TryGetValue(type, result)) return (SlangReflectionTypeLayout*)result.Ptr(); - result = CreateTypeLayout(layoutContext, type); + result = createTypeLayout(layoutContext, type); targetReq->getTypeLayouts()[type] = result; return (SlangReflectionTypeLayout*)result.Ptr(); } diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp index 6040df155..eefcfe1fd 100644 --- a/source/slang/type-layout.cpp +++ b/source/slang/type-layout.cpp @@ -120,12 +120,10 @@ struct DefaultLayoutRulesImpl : SimpleLayoutRulesImpl return vectorInfo; } - SimpleLayoutInfo GetMatrixLayout(SimpleLayoutInfo elementInfo, size_t rowCount, size_t columnCount) override + SimpleArrayLayoutInfo GetMatrixLayout(SimpleLayoutInfo elementInfo, size_t rowCount, size_t columnCount) override { // The default behavior here is to lay out a matrix - // as an array of column vectors (that is column-major). - // That is because this is the default convention - // used by HLSL. + // as an array of row vectors (that is row-major). // // In practice, the code that calls `GetMatrixLayout` will // potentially transpose the row/column counts in order @@ -880,26 +878,50 @@ bool IsResourceKind(LayoutResourceKind kind) } -SimpleLayoutInfo GetSimpleLayoutImpl( + /// A custom tuple to capture the outputs of type layout +struct TypeLayoutResult +{ + /// The actual heap-allocated layout object with all the details + RefPtr<TypeLayout> layout; + + /// A simplified representation of layout information. + /// + /// This information is suitable for the case where a type only + /// consumes a single resource. + /// + SimpleLayoutInfo info; + + /// Default constructor. + TypeLayoutResult() + {} + + /// Construct a result from the given layout object and simple layout info. + TypeLayoutResult(RefPtr<TypeLayout> inLayout, SimpleLayoutInfo const& inInfo) + : layout(inLayout) + , info(inInfo) + {} +}; + + /// Create a type layout for a type that has simple layout needs. + /// + /// This handles any type that can express its layout in `SimpleLayoutInfo`, + /// and that only needs a `TypeLayout` and not a refined subclass. + /// +static TypeLayoutResult createSimpleTypeLayout( SimpleLayoutInfo info, RefPtr<Type> type, - LayoutRulesImpl* rules, - RefPtr<TypeLayout>* outTypeLayout) + LayoutRulesImpl* rules) { - if (outTypeLayout) - { - RefPtr<TypeLayout> typeLayout = new TypeLayout(); - *outTypeLayout = typeLayout; + RefPtr<TypeLayout> typeLayout = new TypeLayout(); - typeLayout->type = type; - typeLayout->rules = rules; + typeLayout->type = type; + typeLayout->rules = rules; - typeLayout->uniformAlignment = info.alignment; + typeLayout->uniformAlignment = info.alignment; - typeLayout->addResourceUsage(info.kind, info.size); - } + typeLayout->addResourceUsage(info.kind, info.size); - return info; + return TypeLayoutResult(typeLayout, info); } static SimpleLayoutInfo getParameterGroupLayoutInfo( @@ -1162,8 +1184,7 @@ RefPtr<TypeLayout> applyOffsetToTypeLayout( return newTypeLayout; } -RefPtr<ParameterGroupTypeLayout> -createParameterGroupTypeLayout( +static RefPtr<ParameterGroupTypeLayout> _createParameterGroupTypeLayout( TypeLayoutContext const& context, RefPtr<ParameterGroupType> parameterGroupType, SimpleLayoutInfo parameterGroupInfo, @@ -1357,23 +1378,21 @@ createParameterGroupTypeLayout( return typeLayout; } -RefPtr<ParameterGroupTypeLayout> -createParameterGroupTypeLayout( +RefPtr<ParameterGroupTypeLayout> createParameterGroupTypeLayout( TypeLayoutContext const& context, RefPtr<ParameterGroupType> parameterGroupType, LayoutRulesImpl* parameterGroupRules, SimpleLayoutInfo parameterGroupInfo, RefPtr<TypeLayout> elementTypeLayout) { - return createParameterGroupTypeLayout( + return _createParameterGroupTypeLayout( context.with(parameterGroupRules).with(context.targetReq->getDefaultMatrixLayoutMode()), parameterGroupType, parameterGroupInfo, elementTypeLayout); } -RefPtr<ParameterGroupTypeLayout> -createParameterGroupTypeLayout( +static RefPtr<ParameterGroupTypeLayout> _createParameterGroupTypeLayout( TypeLayoutContext const& context, RefPtr<ParameterGroupType> parameterGroupType, RefPtr<Type> elementType, @@ -1408,7 +1427,7 @@ createParameterGroupTypeLayout( context.with(elementTypeRules), elementType); - return createParameterGroupTypeLayout( + return _createParameterGroupTypeLayout( context, parameterGroupType, info, @@ -1450,8 +1469,7 @@ LayoutRulesImpl* getParameterBufferElementTypeLayoutRules( } } -RefPtr<ParameterGroupTypeLayout> -createParameterGroupTypeLayout( +RefPtr<ParameterGroupTypeLayout> createParameterGroupTypeLayout( TypeLayoutContext const& context, RefPtr<ParameterGroupType> parameterGroupType) { @@ -1464,7 +1482,7 @@ createParameterGroupTypeLayout( auto elementType = parameterGroupType->elementType; - return createParameterGroupTypeLayout( + return _createParameterGroupTypeLayout( context, parameterGroupType, elementType, @@ -1519,7 +1537,7 @@ createStructuredBufferTypeLayout( LayoutRule::HLSLStructuredBuffer); // Create and save type layout for the buffer contents. - auto elementTypeLayout = CreateTypeLayout( + auto elementTypeLayout = createTypeLayout( context.with(structuredBufferLayoutRules), elementType.Ptr()); @@ -1531,21 +1549,41 @@ createStructuredBufferTypeLayout( } -SimpleLayoutInfo GetLayoutImpl( + /// Create layout information for the given `type`. + /// + /// This internal routine returns both the constructed type + /// layout object and the simple layout info, encapsulated + /// together as a `TypeLayoutResult`. + /// +static TypeLayoutResult _createTypeLayout( TypeLayoutContext const& context, - Type* type, - RefPtr<TypeLayout>* outTypeLayout); + Type* type); -SimpleLayoutInfo GetLayoutImpl( + /// Create layout information for the given `type`, obeying any layout modifiers on the given declaration. + /// + /// If `declForModifiers` has any matrix layout modifiers associated with it, then + /// the resulting type layout will respect those modifiers. + /// +static TypeLayoutResult _createTypeLayout( TypeLayoutContext const& context, Type* type, - RefPtr<TypeLayout>* outTypeLayout, Decl* declForModifiers) { TypeLayoutContext subContext = context; if (declForModifiers) { + // TODO: The approach implemented here has a row/column-major + // layout model recursively affect any sub-fields (so that + // the layout of a nested struct depends on the context where + // it is nested). This is consistent with the GLSL behavior + // for these modifiers, but it is *not* how HLSL is supposed + // to work. + // + // In the trivial case where `row_major` and `column_major` + // are only applied to leaf fields/variables of matrix type + // the difference should be immaterial. + if (declForModifiers->HasModifier<RowMajorLayoutModifier>()) subContext.matrixLayoutMode = kMatrixLayoutMode_RowMajor; @@ -1556,7 +1594,7 @@ SimpleLayoutInfo GetLayoutImpl( // layout, such as GLSL `std140`. } - return GetLayoutImpl(subContext, type, outTypeLayout); + return _createTypeLayout(subContext, type); } int findGenericParam(List<RefPtr<GenericParamLayout>> & genericParameters, GlobalGenericParamDecl * decl) @@ -1824,10 +1862,9 @@ static RefPtr<TypeLayout> maybeAdjustLayoutForArrayElementType( } } -SimpleLayoutInfo GetLayoutImpl( +static TypeLayoutResult _createTypeLayout( TypeLayoutContext const& context, - Type* type, - RefPtr<TypeLayout>* outTypeLayout) + Type* type) { auto rules = context.rules; @@ -1851,22 +1888,18 @@ SimpleLayoutInfo GetLayoutImpl( // the constant buffer, which need to be surfaces out // to the top level. // - if (outTypeLayout) - { - *outTypeLayout = createParameterGroupTypeLayout( - context, - parameterGroupType); - } + auto typeLayout = createParameterGroupTypeLayout( + context, + parameterGroupType); - return info; + return TypeLayoutResult(typeLayout, info); } else if (auto samplerStateType = as<SamplerStateType>(type)) { - return GetSimpleLayoutImpl( + return createSimpleTypeLayout( rules->GetObjectLayout(ShaderParameterKind::SamplerState), type, - rules, - outTypeLayout); + rules); } else if (auto textureType = as<TextureType>(type)) { @@ -1884,11 +1917,10 @@ SimpleLayoutInfo GetLayoutImpl( break; } - return GetSimpleLayoutImpl( + return createSimpleTypeLayout( rules->GetObjectLayout(kind), type, - rules, - outTypeLayout); + rules); } else if (auto imageType = as<GLSLImageType>(type)) { @@ -1906,11 +1938,10 @@ SimpleLayoutInfo GetLayoutImpl( break; } - return GetSimpleLayoutImpl( + return createSimpleTypeLayout( rules->GetObjectLayout(kind), type, - rules, - outTypeLayout); + rules); } else if (auto textureSamplerType = as<TextureSamplerType>(type)) { @@ -1928,26 +1959,22 @@ SimpleLayoutInfo GetLayoutImpl( break; } - return GetSimpleLayoutImpl( + return createSimpleTypeLayout( rules->GetObjectLayout(kind), type, - rules, - outTypeLayout); + rules); } // TODO: need a better way to handle this stuff... #define CASE(TYPE, KIND) \ - else if(auto type_##TYPE = as<TYPE>(type)) do { \ + else if(auto type_##TYPE = as<TYPE>(type)) do { \ auto info = rules->GetObjectLayout(ShaderParameterKind::KIND); \ - if (outTypeLayout) \ - { \ - *outTypeLayout = createStructuredBufferTypeLayout( \ + auto typeLayout = createStructuredBufferTypeLayout( \ context, \ ShaderParameterKind::KIND, \ type_##TYPE, \ type_##TYPE->elementType.Ptr()); \ - } \ - return info; \ + return TypeLayoutResult(typeLayout, info); \ } while(0) CASE(HLSLStructuredBufferType, StructuredBuffer); @@ -1962,9 +1989,9 @@ SimpleLayoutInfo GetLayoutImpl( // TODO: need a better way to handle this stuff... #define CASE(TYPE, KIND) \ else if(as<TYPE>(type)) do { \ - return GetSimpleLayoutImpl( \ + return createSimpleTypeLayout( \ rules->GetObjectLayout(ShaderParameterKind::KIND), \ - type, rules, outTypeLayout); \ + type, rules); \ } while(0) CASE(HLSLByteAddressBufferType, RawBuffer); @@ -1978,75 +2005,118 @@ SimpleLayoutInfo GetLayoutImpl( #undef CASE - // - // TODO(tfoley): Need to recognize any UAV types here - // else if(auto basicType = as<BasicExpressionType>(type)) { - return GetSimpleLayoutImpl( + return createSimpleTypeLayout( rules->GetScalarLayout(basicType->baseType), type, - rules, - outTypeLayout); + rules); } else if(auto vecType = as<VectorExpressionType>(type)) { - return GetSimpleLayoutImpl( - rules->GetVectorLayout( - GetLayout(context, vecType->elementType.Ptr()), - (size_t) GetIntVal(vecType->elementCount)), - type, - rules, - outTypeLayout); + auto elementType = vecType->elementType; + size_t elementCount = (size_t) GetIntVal(vecType->elementCount); + + auto element = _createTypeLayout( + context, + elementType); + + auto info = rules->GetVectorLayout(element.info, elementCount); + + RefPtr<VectorTypeLayout> typeLayout = new VectorTypeLayout(); + typeLayout->type = type; + typeLayout->rules = rules; + typeLayout->uniformAlignment = info.alignment; + + typeLayout->elementTypeLayout = element.layout; + typeLayout->uniformStride = element.info.getUniformLayout().size.getFiniteValue(); + + typeLayout->addResourceUsage(info.kind, info.size); + + return TypeLayoutResult(typeLayout, info); } else if(auto matType = as<MatrixExpressionType>(type)) { - // The `GetMatrixLayout` implementation in the layout rules - // currently defaults to assuming column-major layout, - // so if we want row-major layout we achieve it here by - // transposing the row/column counts. - // - // TODO: If it is really a universal convention that matrices - // are laid out just like arrays of vectors, when we can - // probably eliminate the `virtual` `GetLayout` method entirely, - // and have the code here be responsible for the layout choice. - // size_t rowCount = (size_t) GetIntVal(matType->getRowCount()); size_t colCount = (size_t) GetIntVal(matType->getColumnCount()); + + auto elementType = matType->getElementType(); + auto elementResult = _createTypeLayout( + context, + elementType); + auto elementTypeLayout = elementResult.layout; + auto elementInfo = elementResult.info; + + // The `GetMatrixLayout` implementation in the layout rules + // currently defaults to assuming row-major layout, + // so if we want column-major layout we achieve it here by + // transposing the major/minor axes counts. + // + size_t layoutMajorCount = rowCount; + size_t layoutMinorCount = colCount; if (context.matrixLayoutMode == kMatrixLayoutMode_ColumnMajor) { - size_t tmp = rowCount; - rowCount = colCount; - colCount = tmp; + size_t tmp = layoutMajorCount; + layoutMajorCount = layoutMinorCount; + layoutMinorCount = tmp; } - auto info = rules->GetMatrixLayout( - GetLayout(context, matType->getElementType()), - rowCount, + elementInfo, + layoutMajorCount, + layoutMinorCount); + + auto rowType = matType->getRowType(); + RefPtr<VectorTypeLayout> rowTypeLayout = new VectorTypeLayout(); + + auto rowInfo = rules->GetVectorLayout( + elementInfo, colCount); - if (outTypeLayout) + size_t majorStride = info.elementStride; + size_t minorStride = elementInfo.getUniformLayout().size.getFiniteValue(); + + size_t rowStride = 0; + size_t colStride = 0; + if(context.matrixLayoutMode == kMatrixLayoutMode_ColumnMajor) { - RefPtr<MatrixTypeLayout> typeLayout = new MatrixTypeLayout(); - *outTypeLayout = typeLayout; + colStride = majorStride; + rowStride = minorStride; + } + else + { + rowStride = majorStride; + colStride = minorStride; + } - typeLayout->type = type; - typeLayout->rules = rules; - typeLayout->uniformAlignment = info.alignment; - typeLayout->mode = context.matrixLayoutMode; + rowTypeLayout->type = type; + rowTypeLayout->rules = rules; + rowTypeLayout->uniformAlignment = elementInfo.getUniformLayout().alignment; - typeLayout->addResourceUsage(info.kind, info.size); - } + rowTypeLayout->uniformStride = colStride; + rowTypeLayout->elementTypeLayout = elementTypeLayout; + rowTypeLayout->addResourceUsage(rowInfo.kind, rowInfo.size); - return info; + RefPtr<MatrixTypeLayout> typeLayout = new MatrixTypeLayout(); + + typeLayout->type = type; + typeLayout->rules = rules; + typeLayout->uniformAlignment = info.alignment; + + typeLayout->elementTypeLayout = rowTypeLayout; + typeLayout->uniformStride = rowStride; + typeLayout->mode = context.matrixLayoutMode; + + typeLayout->addResourceUsage(info.kind, info.size); + + return TypeLayoutResult(typeLayout, info); } else if (auto arrayType = as<ArrayExpressionType>(type)) { - RefPtr<TypeLayout> elementTypeLayout; - auto elementInfo = GetLayoutImpl( + auto elementResult = _createTypeLayout( context, - arrayType->baseType.Ptr(), - outTypeLayout ? &elementTypeLayout : nullptr); + arrayType->baseType.Ptr()); + auto elementInfo = elementResult.info; + auto elementTypeLayout = elementResult.layout; // To a first approximation, an array will usually be laid out // by taking the element's type layout and laying out `elementCount` @@ -2073,124 +2143,121 @@ SimpleLayoutInfo GetLayoutImpl( elementInfo, elementCount).getUniformLayout(); - if (outTypeLayout) - { - RefPtr<ArrayTypeLayout> typeLayout = new ArrayTypeLayout(); - *outTypeLayout = typeLayout; + RefPtr<ArrayTypeLayout> typeLayout = new ArrayTypeLayout(); - // Some parts of the array type layout object are easy to fill in: - typeLayout->type = type; - typeLayout->rules = rules; - typeLayout->originalElementTypeLayout = elementTypeLayout; - typeLayout->uniformAlignment = arrayUniformInfo.alignment; - typeLayout->uniformStride = arrayUniformInfo.elementStride; + // Some parts of the array type layout object are easy to fill in: + typeLayout->type = type; + typeLayout->rules = rules; + typeLayout->originalElementTypeLayout = elementTypeLayout; + typeLayout->uniformAlignment = arrayUniformInfo.alignment; + typeLayout->uniformStride = arrayUniformInfo.elementStride; - typeLayout->addResourceUsage(LayoutResourceKind::Uniform, arrayUniformInfo.size); + typeLayout->addResourceUsage(LayoutResourceKind::Uniform, arrayUniformInfo.size); + // + // 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 + // account. 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 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 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. // - // The `maybeAdjustLayoutForArrayElementType` computes an "adjusted" - // type layout for the element type which takes the array stride into - // account. If it returns the same type layout that was passed in, - // then that means no adjustement took place. + if (elementResourceInfo.kind == LayoutResourceKind::DescriptorTableSlot) + { + arrayResourceCount = elementResourceInfo.count; + } // - // The `additionalSpacesNeededForAdjustedElementType` variable counts - // the number of additional register spaces that were consumed, - // in the case of an unbounded array. + // 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. // - 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. + // 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. // - for( auto elementResourceInfo : elementTypeLayout->resourceInfos ) + else if( + elementCount.isInfinite() + && adjustedElementTypeLayout != elementTypeLayout + && doesResourceRequireAdjustmentForArrayOfStructs(elementResourceInfo.kind) ) { - // 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 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. - // - 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); + // 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. } - - // 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 ) + else { - typeLayout->addResourceUsage(LayoutResourceKind::RegisterSpace, additionalSpacesNeededForAdjustedElementType); + 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); } - return arrayUniformInfo; + + // 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 TypeLayoutResult(typeLayout, arrayUniformInfo); } else if (auto declRefType = as<DeclRefType>(type)) { @@ -2198,14 +2265,9 @@ SimpleLayoutInfo GetLayoutImpl( if (auto structDeclRef = declRef.as<StructDecl>()) { - RefPtr<StructTypeLayout> typeLayout; - if (outTypeLayout) - { - typeLayout = new StructTypeLayout(); - typeLayout->type = type; - typeLayout->rules = rules; - *outTypeLayout = typeLayout; - } + RefPtr<StructTypeLayout> typeLayout = new StructTypeLayout(); + typeLayout->type = type; + typeLayout->rules = rules; // The layout of a `struct` type is computed in the somewhat // obvious fashion by keeping a running counter of the resource @@ -2219,12 +2281,12 @@ SimpleLayoutInfo GetLayoutImpl( for (auto field : GetFields(structDeclRef)) { - RefPtr<TypeLayout> fieldTypeLayout; - UniformLayoutInfo fieldInfo = GetLayoutImpl( + TypeLayoutResult fieldResult = _createTypeLayout( context, GetType(field).Ptr(), - outTypeLayout ? &fieldTypeLayout : nullptr, - field.getDecl()).getUniformLayout(); + field.getDecl()); + RefPtr<TypeLayout> fieldTypeLayout = fieldResult.layout; + UniformLayoutInfo fieldInfo = fieldResult.info.getUniformLayout(); // Note: we don't add any zero-size fields // when computing structure layout, just @@ -2240,81 +2302,75 @@ SimpleLayoutInfo GetLayoutImpl( uniformOffset = rules->AddStructField(&info, fieldInfo); } - if (outTypeLayout) + // We need to create variable layouts + // for each field of the structure. + RefPtr<VarLayout> fieldLayout = new VarLayout(); + fieldLayout->varDecl = field; + fieldLayout->typeLayout = fieldTypeLayout; + typeLayout->fields.Add(fieldLayout); + typeLayout->mapVarToLayout.Add(field.getDecl(), fieldLayout); + + // Set up uniform offset information, if there is any uniform data in the field + if( fieldTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform) ) { - // If we are computing a complete layout, - // then we need to create variable layouts - // for each field of the structure. - RefPtr<VarLayout> fieldLayout = new VarLayout(); - fieldLayout->varDecl = field; - fieldLayout->typeLayout = fieldTypeLayout; - typeLayout->fields.Add(fieldLayout); - typeLayout->mapVarToLayout.Add(field.getDecl(), fieldLayout); - - // 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.getFiniteValue(); - } + fieldLayout->AddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset.getFiniteValue(); + } - // Add offset information for any other resource kinds - for( auto fieldTypeResourceInfo : fieldTypeLayout->resourceInfos ) + // Add offset information for any other resource kinds + for( auto fieldTypeResourceInfo : fieldTypeLayout->resourceInfos ) + { + // Uniforms were dealt with above + if(fieldTypeResourceInfo.kind == LayoutResourceKind::Uniform) + continue; + + // We should not have already processed this resource type + SLANG_RELEASE_ASSERT(!fieldLayout->FindResourceInfo(fieldTypeResourceInfo.kind)); + + // The field will need offset information for this kind + auto fieldResourceInfo = fieldLayout->AddResourceInfo(fieldTypeResourceInfo.kind); + + // 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() ) { - // Uniforms were dealt with above - if(fieldTypeResourceInfo.kind == LayoutResourceKind::Uniform) - continue; - - // We should not have already processed this resource type - SLANG_RELEASE_ASSERT(!fieldLayout->FindResourceInfo(fieldTypeResourceInfo.kind)); - - // The field will need offset information for this kind - auto fieldResourceInfo = fieldLayout->AddResourceInfo(fieldTypeResourceInfo.kind); + // 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; - // 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. + // The field itself will record itself as having a zero offset into + // the chosen space. // - // We handle this case by allocating a distinct register space for - // any field that consumes an unbounded amount of registers. + 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. // - 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; - } + auto structTypeResourceInfo = typeLayout->findOrAddResourceInfo(fieldTypeResourceInfo.kind); + fieldResourceInfo->index = structTypeResourceInfo->count.getFiniteValue(); + structTypeResourceInfo->count += fieldTypeResourceInfo.count; } } } rules->EndStructLayout(&info); - if (outTypeLayout) - { - typeLayout->uniformAlignment = info.alignment; - typeLayout->addResourceUsage(LayoutResourceKind::Uniform, info.size); - } - return info; + typeLayout->uniformAlignment = info.alignment; + typeLayout->addResourceUsage(LayoutResourceKind::Uniform, info.size); + + return TypeLayoutResult(typeLayout, info); } else if (auto globalGenParam = declRef.as<GlobalGenericParamDecl>()) { @@ -2322,18 +2378,16 @@ SimpleLayoutInfo GetLayoutImpl( info.alignment = 0; info.size = 0; info.kind = LayoutResourceKind::GenericResource; - if (outTypeLayout) - { - auto genParamTypeLayout = new GenericParamTypeLayout(); - // we should have already populated ProgramLayout::genericEntryPointParams list at this point, - // so we can find the index of this generic param decl in the list - genParamTypeLayout->type = type; - genParamTypeLayout->paramIndex = findGenericParam(context.programLayout->globalGenericParams, genParamTypeLayout->getGlobalGenericParamDecl()); - genParamTypeLayout->rules = rules; - genParamTypeLayout->findOrAddResourceInfo(LayoutResourceKind::GenericResource)->count += 1; - *outTypeLayout = genParamTypeLayout; - } - return info; + + auto genParamTypeLayout = new GenericParamTypeLayout(); + // we should have already populated ProgramLayout::genericEntryPointParams list at this point, + // so we can find the index of this generic param decl in the list + genParamTypeLayout->type = type; + genParamTypeLayout->paramIndex = findGenericParam(context.programLayout->globalGenericParams, genParamTypeLayout->getGlobalGenericParamDecl()); + genParamTypeLayout->rules = rules; + genParamTypeLayout->findOrAddResourceInfo(LayoutResourceKind::GenericResource)->count += 1; + + return TypeLayoutResult(genParamTypeLayout, info); } else if( auto simpleGenericParam = declRef.as<GenericTypeParamDecl>() ) { @@ -2350,12 +2404,10 @@ SimpleLayoutInfo GetLayoutImpl( // any parameters, even those that don't depend on // generics. // - SimpleLayoutInfo info; - return GetSimpleLayoutImpl( - info, + return createSimpleTypeLayout( + SimpleLayoutInfo(), type, - rules, - outTypeLayout); + rules); } else if( auto interfaceDeclRef = declRef.as<InterfaceDecl>() ) { @@ -2373,25 +2425,22 @@ SimpleLayoutInfo GetLayoutImpl( // represents the indirections needed to reference the // data to be referenced by this field. // - return GetSimpleLayoutImpl( + return createSimpleTypeLayout( SimpleLayoutInfo(LayoutResourceKind::ExistentialSlot, 1), type, - rules, - outTypeLayout); + rules); } } else if (auto errorType = as<ErrorType>(type)) { // An error type means that we encountered something we don't understand. // - // We should probalby inform the user with an error message here. + // We should probably inform the user with an error message here. - SimpleLayoutInfo info; - return GetSimpleLayoutImpl( - info, + return createSimpleTypeLayout( + SimpleLayoutInfo(), type, - rules, - outTypeLayout); + rules); } else if( auto taggedUnionType = as<TaggedUnionType>(type) ) { @@ -2408,47 +2457,34 @@ SimpleLayoutInfo GetLayoutImpl( // UniformLayoutInfo info(0, 1); - // If we are being asked to construct a full `TypeLayout` - // object, then we'll allocate it up front. - // - RefPtr<TaggedUnionTypeLayout> taggedUnionLayout; - if( outTypeLayout ) - { - taggedUnionLayout = new TaggedUnionTypeLayout(); - taggedUnionLayout->type = type; - taggedUnionLayout->rules = rules; - *outTypeLayout = taggedUnionLayout; - } + RefPtr<TaggedUnionTypeLayout> taggedUnionLayout = new TaggedUnionTypeLayout(); + taggedUnionLayout->type = type; + taggedUnionLayout->rules = rules; // Now we iterate over the case types and see if they // change our computed maximum size/alignement. // for( auto caseType : taggedUnionType->caseTypes ) { - RefPtr<TypeLayout> caseTypeLayout; - UniformLayoutInfo caseTypeInfo = GetLayoutImpl(context, caseType, outTypeLayout ? &caseTypeLayout : nullptr).getUniformLayout(); + auto caseTypeResult = _createTypeLayout(context, caseType); + RefPtr<TypeLayout> caseTypeLayout = caseTypeResult.layout; + UniformLayoutInfo caseTypeInfo = caseTypeResult.info.getUniformLayout(); info.size = maximum(info.size, caseTypeInfo.size); info.alignment = std::max(info.alignment, caseTypeInfo.alignment); - // If we are building a full `TypeLayout` we need to - // do a few more steps for each case type. + // We need to remember the layout of the case type + // on the final `TaggedUnionTypeLayout`. // - if( outTypeLayout ) - { - // We need to remember the layout of the case type - // on the final `TaggedUnionTypeLayout`. - // - taggedUnionLayout->caseTypeLayouts.Add(caseTypeLayout); + taggedUnionLayout->caseTypeLayouts.Add(caseTypeLayout); - // We also need to consider contributions for other - // resource kinds beyond uniform data. - // - for( auto caseResInfo : caseTypeLayout->resourceInfos ) - { - auto unionResInfo = taggedUnionLayout->findOrAddResourceInfo(caseResInfo.kind); - unionResInfo->count = maximum(unionResInfo->count, caseResInfo.count); - } + // We also need to consider contributions for other + // resource kinds beyond uniform data. + // + for( auto caseResInfo : caseTypeLayout->resourceInfos ) + { + auto unionResInfo = taggedUnionLayout->findOrAddResourceInfo(caseResInfo.kind); + unionResInfo->count = maximum(unionResInfo->count, caseResInfo.count); } } @@ -2467,10 +2503,7 @@ SimpleLayoutInfo GetLayoutImpl( auto tagInfo = context.rules->GetScalarLayout(BaseType::UInt); info.size = RoundToAlignment(info.size, tagInfo.alignment); - if( outTypeLayout ) - { - taggedUnionLayout->tagOffset = info.size; - } + taggedUnionLayout->tagOffset = info.size; info.size += tagInfo.size; info.alignment = std::max(info.alignment, tagInfo.alignment); @@ -2480,48 +2513,178 @@ SimpleLayoutInfo GetLayoutImpl( // we will make sure that its information on uniform layout // matches what we've computed in the `UniformLayoutInfo` we return. // - if( outTypeLayout ) - { - taggedUnionLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->count = info.size; - taggedUnionLayout->uniformAlignment = info.alignment; - } + taggedUnionLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->count = info.size; + taggedUnionLayout->uniformAlignment = info.alignment; - return info; + return TypeLayoutResult(taggedUnionLayout, info); } // catch-all case in case nothing matched - SLANG_ASSERT(!"unimplemented"); - SimpleLayoutInfo info; - return GetSimpleLayoutImpl( - info, + SLANG_ASSERT(!"unimplemented case in type layout"); + return createSimpleTypeLayout( + SimpleLayoutInfo(), type, - rules, - outTypeLayout); + rules); } -SimpleLayoutInfo GetLayout( - TypeLayoutContext const& context, - Type* inType) +RefPtr<TypeLayout> getSimpleVaryingParameterTypeLayout( + TypeLayoutContext const& context, + Type* type, + EntryPointParameterDirectionMask directionMask) { - return GetLayoutImpl(context, inType, nullptr); -} + auto rules = context.rules; -RefPtr<TypeLayout> createTypeLayout( - TypeLayoutContext const& context, - Type* type) -{ - RefPtr<TypeLayout> typeLayout; - GetLayoutImpl(context, type, &typeLayout); - return typeLayout; + // TODO: This logic should ideally share as much + // as possible with the `_createTypeLayout` function, + // to avoid duplication, but we also have to deal + // with the many ways in which varying parameter + // layout differs from non-varying layout. + + // We will compute resource consumption for the type + // as a varying input, output, or both/neither. + // To avoid duplication, we'll build an array that + // includes all the layout rules we need to apply. + // + int varyingRulesCount = 0; + LayoutRulesImpl* varyingRules[2]; + + if( directionMask & kEntryPointParameterDirection_Input ) + { + varyingRules[varyingRulesCount++] = context.getRulesFamily()->getVaryingInputRules(); + } + if( directionMask & kEntryPointParameterDirection_Output ) + { + varyingRules[varyingRulesCount++] = context.getRulesFamily()->getVaryingOutputRules(); + } + + if(auto basicType = as<BasicExpressionType>(type)) + { + auto baseType = basicType->baseType; + + RefPtr<TypeLayout> typeLayout = new TypeLayout(); + typeLayout->type = type; + typeLayout->rules = rules; + + for( int rr = 0; rr < varyingRulesCount; ++rr ) + { + auto info = varyingRules[rr]->GetScalarLayout(baseType); + typeLayout->addResourceUsage(info.kind, info.size); + } + + return typeLayout; + } + else if(auto vecType = as<VectorExpressionType>(type)) + { + auto elementType = vecType->elementType; + size_t elementCount = (size_t) GetIntVal(vecType->elementCount); + + BaseType elementBaseType = BaseType::Void; + if( auto elementBasicType = as<BasicExpressionType>(elementType) ) + { + elementBaseType = elementBasicType->baseType; + } + + // Note that we do *not* add any resource usage to the type + // layout for the element type, because we currently cannot count + // varying parameter usage at a granularity finer than + // individual "locations." + // + RefPtr<TypeLayout> elementTypeLayout = new TypeLayout(); + elementTypeLayout->type = elementType; + elementTypeLayout->rules = rules; + + RefPtr<VectorTypeLayout> typeLayout = new VectorTypeLayout(); + typeLayout->type = vecType; + typeLayout->rules = rules; + typeLayout->elementTypeLayout = elementTypeLayout; + + for( int rr = 0; rr < varyingRulesCount; ++rr ) + { + auto varyingRuleSet = varyingRules[rr]; + auto elementInfo = varyingRuleSet->GetScalarLayout(elementBaseType); + auto info = varyingRuleSet->GetVectorLayout(elementInfo, elementCount); + typeLayout->addResourceUsage(info.kind, info.size); + } + + return typeLayout; + } + else if(auto matType = as<MatrixExpressionType>(type)) + { + size_t rowCount = (size_t) GetIntVal(matType->getRowCount()); + size_t colCount = (size_t) GetIntVal(matType->getColumnCount()); + auto elementType = matType->getElementType(); + + BaseType elementBaseType = BaseType::Void; + if( auto elementBasicType = as<BasicExpressionType>(elementType) ) + { + elementBaseType = elementBasicType->baseType; + } + + // Just as for `_createTypeLayout`, we need to handle row- and + // column-major matrices differently, to ensure we get + // the expected layout. + // + // A varying parameter with row-major layout is effectively + // just an array of row vectors, while a column-major one + // is just an array of column vectors. + // + size_t layoutMajorCount = rowCount; + size_t layoutMinorCount = colCount; + if (context.matrixLayoutMode == kMatrixLayoutMode_ColumnMajor) + { + size_t tmp = layoutMajorCount; + layoutMajorCount = layoutMinorCount; + layoutMinorCount = tmp; + } + + RefPtr<TypeLayout> elementTypeLayout = new TypeLayout(); + elementTypeLayout->type = elementType; + elementTypeLayout->rules = rules; + + RefPtr<VectorTypeLayout> rowTypeLayout = new VectorTypeLayout(); + rowTypeLayout->type = matType->getRowType(); + rowTypeLayout->rules = rules; + rowTypeLayout->elementTypeLayout = elementTypeLayout; + + RefPtr<MatrixTypeLayout> typeLayout = new MatrixTypeLayout(); + typeLayout->type = type; + typeLayout->rules = rules; + typeLayout->elementTypeLayout = rowTypeLayout; + typeLayout->mode = context.matrixLayoutMode; + + for( int rr = 0; rr < varyingRulesCount; ++rr ) + { + auto varyingRuleSet = varyingRules[rr]; + auto elementInfo = varyingRuleSet->GetScalarLayout(elementBaseType); + + auto info = varyingRuleSet->GetMatrixLayout(elementInfo, layoutMajorCount, layoutMinorCount); + typeLayout->addResourceUsage(info.kind, info.size); + + if(context.matrixLayoutMode == kMatrixLayoutMode_RowMajor) + { + // For row-major matrices only, we can compute an effective + // resource usage for the row type. + auto rowInfo = varyingRuleSet->GetVectorLayout(elementInfo, colCount); + rowTypeLayout->addResourceUsage(rowInfo.kind, rowInfo.size); + } + } + + return typeLayout; + } + + // catch-all case in case nothing matched + SLANG_ASSERT(!"unimplemented case for varying parameter layout"); + return createSimpleTypeLayout( + SimpleLayoutInfo(), + type, + rules).layout; } -RefPtr<TypeLayout> CreateTypeLayout( +RefPtr<TypeLayout> createTypeLayout( TypeLayoutContext const& context, Type* type) { - RefPtr<TypeLayout> typeLayout; - GetLayoutImpl(context, type, &typeLayout); - return typeLayout; + return _createTypeLayout(context, type).layout; } RefPtr<TypeLayout> TypeLayout::unwrapArray() diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h index 1d939b18f..b3b1ed1df 100644 --- a/source/slang/type-layout.h +++ b/source/slang/type-layout.h @@ -493,21 +493,33 @@ public: RefPtr<TypeLayout> elementTypeLayout; }; -// Specific case of type layout for an array -class ArrayTypeLayout : public TypeLayout + /// Type layout for a logical sequence type +class SequenceTypeLayout : public TypeLayout { public: - // The original type layout for the array elements, - // which doesn't include any adjustments based on - // resource type splitting. - RefPtr<TypeLayout> originalElementTypeLayout; - - // The *adjusted* layout used for the element type + /// The layout of the element type. + /// + /// This layout may include adjustments to make lookups in elements + /// of the array Just Work, and may not be the same as the layout + /// of the element type when used in a non-array context. + /// RefPtr<TypeLayout> elementTypeLayout; - // the stride between elements when used in - // a uniform buffer - size_t uniformStride; + /// The stride in bytes between elements. + size_t uniformStride = 0; +}; + + /// Type layout for an array type +class ArrayTypeLayout : public SequenceTypeLayout +{ +public: + /// The original layout of the element type. + /// + /// This layout does not include any adjustments that + /// were made to the element type in order to make + /// lookup into array elements Just Work. + /// + RefPtr<TypeLayout> originalElementTypeLayout; }; // type layout for a variable with stream-output type @@ -517,11 +529,23 @@ public: RefPtr<TypeLayout> elementTypeLayout; }; +class VectorTypeLayout : public SequenceTypeLayout +{ +public: +}; + -class MatrixTypeLayout : public TypeLayout +class MatrixTypeLayout : public SequenceTypeLayout { public: - MatrixLayoutMode mode; + /// Is this matrix laid out as row-major or column-major? + /// + /// Note that this does *not* affect the interpretation + /// of the `elementTypeLayout` field, which always represents + /// the logical elements of the matrix type, which are its + /// rows. + /// + MatrixLayoutMode mode; }; // Specific case of type layout for a struct @@ -719,7 +743,7 @@ struct SimpleLayoutRulesImpl // Get layout for a vector or matrix type virtual SimpleLayoutInfo GetVectorLayout(SimpleLayoutInfo elementInfo, size_t elementCount) = 0; - virtual SimpleLayoutInfo GetMatrixLayout(SimpleLayoutInfo elementInfo, size_t rowCount, size_t columnCount) = 0; + virtual SimpleArrayLayoutInfo GetMatrixLayout(SimpleLayoutInfo elementInfo, size_t rowCount, size_t columnCount) = 0; // Begin doing layout on a `struct` type virtual UniformLayoutInfo BeginStructLayout() = 0; @@ -760,7 +784,7 @@ struct LayoutRulesImpl return simpleRules->GetVectorLayout(elementInfo, elementCount); } - SimpleLayoutInfo GetMatrixLayout(SimpleLayoutInfo elementInfo, size_t rowCount, size_t columnCount) + SimpleArrayLayoutInfo GetMatrixLayout(SimpleLayoutInfo elementInfo, size_t rowCount, size_t columnCount) { return simpleRules->GetMatrixLayout(elementInfo, rowCount, columnCount); } @@ -832,7 +856,7 @@ struct TypeLayoutContext MatrixLayoutMode matrixLayoutMode; LayoutRulesImpl* getRules() { return rules; } - LayoutRulesFamilyImpl* getRulesFamily() { return rules->getLayoutRulesFamily(); } + LayoutRulesFamilyImpl* getRulesFamily() const { return rules->getLayoutRulesFamily(); } TypeLayoutContext with(LayoutRulesImpl* inRules) const { @@ -861,52 +885,51 @@ TypeLayoutContext getInitialLayoutContextForTarget( TargetRequest* targetReq, ProgramLayout* programLayout); -// Get the "simple" layout for a type according to a given set of layout -// rules. Note that a "simple" layout can only consume one `LayoutResourceKind`, -// and so this operation may not correctly capture the full resource usage -// of a type. -SimpleLayoutInfo GetLayout( - TypeLayoutContext const& context, - Type* type); + /// Direction(s) of a varying shader parameter +typedef unsigned int EntryPointParameterDirectionMask; +enum +{ + kEntryPointParameterDirection_Input = 0x1, + kEntryPointParameterDirection_Output = 0x2, +}; + + + /// Get layout information for a simple varying parameter type. + /// + /// A simple varying parameter is a scalar, vector, or matrix. + /// +RefPtr<TypeLayout> getSimpleVaryingParameterTypeLayout( + TypeLayoutContext const& context, + Type* type, + EntryPointParameterDirectionMask directionMask); // Create a full type-layout object for a type, // according to the layout rules in `context`. -RefPtr<TypeLayout> CreateTypeLayout( +RefPtr<TypeLayout> createTypeLayout( TypeLayoutContext const& context, Type* type); // Create a full type layout for a type, while applying the given "simple" // layout information as an offset to any `VarLayout`s created along // the way. -RefPtr<TypeLayout> CreateTypeLayout( +RefPtr<TypeLayout> createTypeLayout( TypeLayoutContext const& context, Type* type, SimpleLayoutInfo offset); // -// Create a type layout for a parameter block type. -RefPtr<ParameterGroupTypeLayout> -createParameterGroupTypeLayout( + /// Create a layout for a parameter-group type (a `ConstantBuffer` or `ParameterBlock`). +RefPtr<ParameterGroupTypeLayout> createParameterGroupTypeLayout( TypeLayoutContext const& context, RefPtr<ParameterGroupType> parameterGroupType); -RefPtr<ParameterGroupTypeLayout> -createParameterGroupTypeLayout( - TypeLayoutContext const& context, - RefPtr<ParameterGroupType> parameterGroupType, - RefPtr<Type> elementType, - LayoutRulesImpl* elementTypeRules); - -RefPtr<ParameterGroupTypeLayout> -createParameterGroupTypeLayout( - TypeLayoutContext const& context, - RefPtr<ParameterGroupType> parameterGroupType, - SimpleLayoutInfo parameterGroupInfo, - RefPtr<TypeLayout> elementTypeLayout); - -RefPtr<ParameterGroupTypeLayout> -createParameterGroupTypeLayout( + /// Create a layout for a parameter-group type (a `ConstantBuffer` or `ParameterBlock`). + /// + /// This overload allows the `parameterGroupType` parameter to be null, for cases + /// where an anonymous parameter group needs to be constructed. + /// +RefPtr<ParameterGroupTypeLayout> createParameterGroupTypeLayout( TypeLayoutContext const& context, RefPtr<ParameterGroupType> parameterGroupType, LayoutRulesImpl* parameterGroupRules, |
