diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2017-11-06 10:37:27 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-11-06 10:37:27 -0800 |
| commit | 9919c823938ae929b16efac9d507f6d5eb122bf4 (patch) | |
| tree | 8fc65791d416cffa8267180177d3f3d179679460 /source/slang/type-layout.cpp | |
| parent | 296e89ca4f3d6d99126bf2ee59666bc946add431 (diff) | |
Parameter blocks (#245)
* Rename existing ParameterBlock to ParameterGroup
We are planning to add a new `ParameterBlock<T>` type, which maps to the notion of a "parameter block" as used in the Spire research work.
Unfortunately, the compiler codebase already uses the term `ParameterBlock` as catch-all to encompass all of HLSL `cbuffer`/`tbuffer` and GLSL `uniform`/`buffer`/`in`/`out` blocks (all of which are lexical `{}`-enclosed blocks that define parameters...).
This change instead renames all of the existing concepts over to `ParameterGroup`, which isn't an ideal name, but at least doesn't directly overlap the new terminology or any existing terminology.
The new `ParameterBlockType` case will probably be a subclass of `ParameterGroupType`, since it is a logical extension of the underlying concept.
* Add Shader Model 5.1 profiles
The HLSL `register(..., space0)` syntax is only allowed on "SM5.1" and later profiles (which is supported by the newer version of `d3dcompiler_47.dll` that comes with the Win10 SDK, but not the older version of `d3dcompiler_47.dll` - good luck figuring out which you have!).
This change adds those profiles to our master list of profiles, and nothing else.
* First pass at support for `ParameterBlock<T>`
- Add the type declaration in stdlib
- Add a special case of `ParameterGroupType` for parameter blocks
- Handle parameter blocks in type layout (currently handling them identically to constant buffers for now, which isn't going to be right in the long term)
- Add an IR pass that basically replaces `ParameterBlock<T>` with `T`
- Eventually this should replace it with either `T` or `ConstantBuffer<T>`, depending on whether the layout that was computed required a constant buffer to hold any "free" uniforms
- Add first stab at an IR pass to "scalarize" global variables using aggregate types with resources inside.
- This currently only applies to global variables, so it won't handle things passed through functions, or used as local variables
- It also only supports cases where the references to the original variable are always references to its fields, and not the whole value itself
- Add a single test case that technically passes with this level of support, but probably isn't very representative of what we need from the feature
* Fold parameter-block desugaring into a more complete "type legalization" pass
The basic problem that was arising is that once you desugar `ParameterBlock<T>` into `T`, you then need todeal with splitting `T` into its constituent fields if it contains any resource types.
Handling those transformations by following the usual use-def chains wasn't really helping, because you might need systematic rewriting that can really only be handled bottom-up.
This change adds a new pass that is intended to perform multiple kinds of type "legalization" at once:
- It will turn `ParameterBlock<T>` into `T`
- It may at some point also convert `ConstantBuffer<T>` into `T` as well
- It will turn an value of an aggregate type that contains resources into N different values (one per field)
- As a result of this, it will also deal with AOS-to-SOA conversion of these types
Legalization is applied to *every* function/instruction/value, so that it can make large-scale changes that would be tough to manage with a work list.
This pass needs to be run *after* generics have been fully specialized, so that we know we are always dealing with fully concrete types, so that their legalization for a given target is completely known.
This is still work in progress; there's more to be done to get this working with all our test cases, and finish the remaining `ParameterBlock<T>` work.
* Improve binding/layout information when using parameter blocks
- When doing type layout for a parameter block, don't include the resources consumed by the element type in the resource usage for the parameter block
- Note that this is pretty much identical to how a `ConstantBuffer<T>` does not report any `LayoutResourceKind::Uniform` usage, except that `ParameterBlock<T>` is *also* going to hide underlying texture/sampler reigster usage
- The one exception here is that any nested items that use up entire `space`s or `set`s those need to be exposed in the resource usage of the parent (I don't have a test for this)
- When type legalization needs to scalarize things, it must propagate layout information down to the new leaf variables. In general, the register/index for a new leaf parameter should be the sum of the offsets for all of the parent variables along the "chain" from the original variable down to the leaf (we aren't dealing with arrays here just yet).
- When type legalization decides to eliminate a pointer(-like) type (e.g., desugar `ParameterBlock<T>` over to `T`), actually deal with that in terms of the `LegalVal`s created, so that we can know to turn a `load` into a no-op when applied to a value that got indirection removed.
- Hack up the "complex" parameter-block test so that it actually passes (the big hack here is that the HLSL baseline is using names that are generated by the IR, and are unlikely to be stable as we add/remove transformations).
- Note: I can't make these be compute tests right now, because regsiter spaces/sets are a feature of D3D12/Vulkan, and our test runner isn't using those APIs.
Diffstat (limited to 'source/slang/type-layout.cpp')
| -rw-r--r-- | source/slang/type-layout.cpp | 180 |
1 files changed, 115 insertions, 65 deletions
diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp index 822311f60..9f2aee922 100644 --- a/source/slang/type-layout.cpp +++ b/source/slang/type-layout.cpp @@ -427,6 +427,7 @@ struct GLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl virtual LayoutRulesImpl* getVaryingOutputRules() override; virtual LayoutRulesImpl* getSpecializationConstantRules() override; virtual LayoutRulesImpl* getShaderStorageBufferRules() override; + virtual LayoutRulesImpl* getParameterBlockRules() override; virtual MatrixLayoutMode getDefaultMatrixLayoutMode() override { @@ -457,6 +458,7 @@ struct HLSLLayoutRulesFamilyImpl : LayoutRulesFamilyImpl virtual LayoutRulesImpl* getVaryingOutputRules() override; virtual LayoutRulesImpl* getSpecializationConstantRules() override; virtual LayoutRulesImpl* getShaderStorageBufferRules() override; + virtual LayoutRulesImpl* getParameterBlockRules() override; virtual MatrixLayoutMode getDefaultMatrixLayoutMode() override { @@ -519,6 +521,12 @@ LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getConstantBufferRules() return &kStd140LayoutRulesImpl_; } +LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getParameterBlockRules() +{ + // TODO: actually pick something appropriate + return &kStd140LayoutRulesImpl_; +} + LayoutRulesImpl* GLSLLayoutRulesFamilyImpl::getPushConstantBufferRules() { return &kGLSLPushConstantLayoutRulesImpl_; @@ -556,6 +564,13 @@ LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getConstantBufferRules() return &kHLSLConstantBufferLayoutRulesImpl_; } +LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getParameterBlockRules() +{ + // TODO: actually pick something appropriate... + return &kHLSLConstantBufferLayoutRulesImpl_; +} + + LayoutRulesImpl* HLSLLayoutRulesFamilyImpl::getPushConstantBufferRules() { return &kHLSLConstantBufferLayoutRulesImpl_; @@ -685,8 +700,8 @@ SimpleLayoutInfo GetSimpleLayoutImpl( return info; } -static SimpleLayoutInfo getParameterBlockLayoutInfo( - RefPtr<ParameterBlockType> type, +static SimpleLayoutInfo getParameterGroupLayoutInfo( + RefPtr<ParameterGroupType> type, LayoutRulesImpl* rules) { if( type->As<ConstantBufferType>() ) @@ -701,14 +716,22 @@ static SimpleLayoutInfo getParameterBlockLayoutInfo( { return rules->GetObjectLayout(ShaderParameterKind::ShaderStorageBuffer); } + else if (type->As<ParameterBlockType>()) + { + // TODO(tfoley): Should a parameter block *always* consume at least + // one `set`/`space`, or should we hold back and just allocate this + // if it actually contains anything? + return SimpleLayoutInfo(LayoutResourceKind::ParameterBlock, 1); + } + // TODO: the vertex-input and fragment-output cases should // only actually apply when we are at the appropriate stage in // the pipeline... - else if( type->As<GLSLInputParameterBlockType>() ) + else if( type->As<GLSLInputParameterGroupType>() ) { return SimpleLayoutInfo(LayoutResourceKind::VertexInput, 0); } - else if( type->As<GLSLOutputParameterBlockType>() ) + else if( type->As<GLSLOutputParameterGroupType>() ) { return SimpleLayoutInfo(LayoutResourceKind::FragmentOutput, 0); } @@ -736,19 +759,19 @@ RefPtr<TypeLayout> createTypeLayout( Type* type, SimpleLayoutInfo offset); -RefPtr<ParameterBlockTypeLayout> -createParameterBlockTypeLayout( +RefPtr<ParameterGroupTypeLayout> +createParameterGroupTypeLayout( TypeLayoutContext* context, - RefPtr<ParameterBlockType> parameterBlockType, - SimpleLayoutInfo parameterBlockInfo, + RefPtr<ParameterGroupType> parameterGroupType, + SimpleLayoutInfo parameterGroupInfo, RefPtr<TypeLayout> elementTypeLayout) { - auto parameterBlockRules = context->rules; + auto parameterGroupRules = context->rules; - auto typeLayout = new ParameterBlockTypeLayout(); + auto typeLayout = new ParameterGroupTypeLayout(); - typeLayout->type = parameterBlockType; - typeLayout->rules = parameterBlockRules; + typeLayout->type = parameterGroupType; + typeLayout->rules = parameterGroupRules; typeLayout->elementTypeLayout = elementTypeLayout; @@ -757,7 +780,7 @@ createParameterBlockTypeLayout( // originally (which should be a single binding "slot" // and hence no uniform data). // - typeLayout->uniformAlignment = parameterBlockInfo.alignment; + typeLayout->uniformAlignment = parameterGroupInfo.alignment; SLANG_RELEASE_ASSERT(!typeLayout->FindResourceInfo(LayoutResourceKind::Uniform)); SLANG_RELEASE_ASSERT(typeLayout->uniformAlignment == 1); @@ -771,73 +794,96 @@ createParameterBlockTypeLayout( // Make sure that we allocate resource usage for the // parameter block itself. - if( parameterBlockInfo.size ) + if( parameterGroupInfo.size ) { typeLayout->addResourceUsage( - parameterBlockInfo.kind, - parameterBlockInfo.size); + parameterGroupInfo.kind, + parameterGroupInfo.size); } - // Now, if the element type itself had any resources, then - // we need to make these part of the layout for our block + // The layout rules for a constant buffer, vs. a "parameter block" + // are different, with respect to how they expose layout information + // for underlying resources. // - // TODO: re-consider this decision, since it creates - // complications... - for( auto elementResourceInfo : elementTypeLayout->resourceInfos ) + // A parameter block should *not* expose the fine-grained resource + // prameters it contains, and should only expose a total number + // of `space`s or `set`s that it consumes. + if (parameterGroupInfo.kind == LayoutResourceKind::ParameterBlock) { - // Skip uniform data, since that is encapsualted behind the constant buffer - if(elementResourceInfo.kind == LayoutResourceKind::Uniform) - break; + // Iterate over element types, but *only* accumulate usage + // info for types that consume whole register sets/spaces. + for( auto elementResourceInfo : elementTypeLayout->resourceInfos ) + { + if(elementResourceInfo.kind != LayoutResourceKind::ParameterBlock) + break; + + typeLayout->addResourceUsage(elementResourceInfo); + } + } + else + { + // In the ordinary case (e.g., a constant buffer) then we need + // to make sure that any resources nested in the element type + // get counted against the container type, so that we can + // allocate registers to it directly. + for( auto elementResourceInfo : elementTypeLayout->resourceInfos ) + { + // Skip uniform data, since that is encapsualted behind the constant buffer + if(elementResourceInfo.kind == LayoutResourceKind::Uniform) + break; - typeLayout->addResourceUsage(elementResourceInfo); + typeLayout->addResourceUsage(elementResourceInfo); + } } + + return typeLayout; } -RefPtr<ParameterBlockTypeLayout> -createParameterBlockTypeLayout( - RefPtr<ParameterBlockType> parameterBlockType, - LayoutRulesImpl* parameterBlockRules, - SimpleLayoutInfo parameterBlockInfo, +RefPtr<ParameterGroupTypeLayout> +createParameterGroupTypeLayout( + RefPtr<ParameterGroupType> parameterGroupType, + LayoutRulesImpl* parameterGroupRules, + SimpleLayoutInfo parameterGroupInfo, RefPtr<TypeLayout> elementTypeLayout) { TypeLayoutContext context; - context.rules = parameterBlockRules; - context.matrixLayoutMode = parameterBlockRules->getDefaultMatrixLayoutMode(); + context.rules = parameterGroupRules; + context.matrixLayoutMode = parameterGroupRules->getDefaultMatrixLayoutMode(); - return createParameterBlockTypeLayout( + return createParameterGroupTypeLayout( &context, - parameterBlockType, - parameterBlockInfo, + parameterGroupType, + parameterGroupInfo, elementTypeLayout); } -RefPtr<ParameterBlockTypeLayout> -createParameterBlockTypeLayout( +RefPtr<ParameterGroupTypeLayout> +createParameterGroupTypeLayout( TypeLayoutContext* context, - RefPtr<ParameterBlockType> parameterBlockType, + RefPtr<ParameterGroupType> parameterGroupType, RefPtr<Type> elementType, LayoutRulesImpl* elementTypeRules) { - auto parameterBlockRules = context->rules; + auto parameterGroupRules = context->rules; // First compute resource usage of the block itself. // For now we assume that the layout of the block can // always be described in a `SimpleLayoutInfo` (only // a single resource kind consumed). SimpleLayoutInfo info; - if (parameterBlockType) + if (parameterGroupType) { - info = getParameterBlockLayoutInfo( - parameterBlockType, - parameterBlockRules); + info = getParameterGroupLayoutInfo( + parameterGroupType, + parameterGroupRules); } else { // If there is no concrete type, then it seems like we are // being asked to compute layout for the global scope - info = parameterBlockRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer); + info = parameterGroupRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer); } // Now compute a layout for the elements of the parameter block. @@ -852,37 +898,41 @@ createParameterBlockTypeLayout( elementType, info); - return createParameterBlockTypeLayout( + return createParameterGroupTypeLayout( context, - parameterBlockType, + parameterGroupType, info, elementTypeLayout); } LayoutRulesImpl* getParameterBufferElementTypeLayoutRules( - RefPtr<ParameterBlockType> parameterBlockType, + RefPtr<ParameterGroupType> parameterGroupType, LayoutRulesImpl* rules) { - if( parameterBlockType->As<ConstantBufferType>() ) + if( parameterGroupType->As<ConstantBufferType>() ) { return rules->getLayoutRulesFamily()->getConstantBufferRules(); } - else if( parameterBlockType->As<TextureBufferType>() ) + else if( parameterGroupType->As<TextureBufferType>() ) { return rules->getLayoutRulesFamily()->getTextureBufferRules(); } - else if( parameterBlockType->As<GLSLInputParameterBlockType>() ) + else if( parameterGroupType->As<GLSLInputParameterGroupType>() ) { return rules->getLayoutRulesFamily()->getVaryingInputRules(); } - else if( parameterBlockType->As<GLSLOutputParameterBlockType>() ) + else if( parameterGroupType->As<GLSLOutputParameterGroupType>() ) { return rules->getLayoutRulesFamily()->getVaryingOutputRules(); } - else if( parameterBlockType->As<GLSLShaderStorageBufferType>() ) + else if( parameterGroupType->As<GLSLShaderStorageBufferType>() ) { return rules->getLayoutRulesFamily()->getShaderStorageBufferRules(); } + else if (parameterGroupType->As<ParameterBlockType>()) + { + return rules->getLayoutRulesFamily()->getParameterBlockRules(); + } else { SLANG_UNEXPECTED("uhandled parameter block type"); @@ -890,23 +940,23 @@ LayoutRulesImpl* getParameterBufferElementTypeLayoutRules( } } -RefPtr<ParameterBlockTypeLayout> -createParameterBlockTypeLayout( +RefPtr<ParameterGroupTypeLayout> +createParameterGroupTypeLayout( TypeLayoutContext* context, - RefPtr<ParameterBlockType> parameterBlockType) + RefPtr<ParameterGroupType> parameterGroupType) { - auto parameterBlockRules = context->rules; + auto parameterGroupRules = context->rules; // Determine the layout rules to use for the contents of the block auto elementTypeRules = getParameterBufferElementTypeLayoutRules( - parameterBlockType, - parameterBlockRules); + parameterGroupType, + parameterGroupRules); - auto elementType = parameterBlockType->elementType; + auto elementType = parameterGroupType->elementType; - return createParameterBlockTypeLayout( + return createParameterGroupTypeLayout( context, - parameterBlockType, + parameterGroupType, elementType, elementTypeRules); } @@ -1015,14 +1065,14 @@ SimpleLayoutInfo GetLayoutImpl( { auto rules = context->rules; - if (auto parameterBlockType = type->As<ParameterBlockType>()) + if (auto parameterGroupType = type->As<ParameterGroupType>()) { // If the user is just interested in uniform layout info, // then this is easy: a `ConstantBuffer<T>` is really no // different from a `Texture2D<U>` in terms of how it // should be handled as a member of a container. // - auto info = getParameterBlockLayoutInfo(parameterBlockType, rules); + auto info = getParameterGroupLayoutInfo(parameterGroupType, rules); // The more interesting case, though, is when the user // is requesting us to actually create a `TypeLayout`, @@ -1037,9 +1087,9 @@ SimpleLayoutInfo GetLayoutImpl( // if (outTypeLayout) { - *outTypeLayout = createParameterBlockTypeLayout( + *outTypeLayout = createParameterGroupTypeLayout( context, - parameterBlockType); + parameterGroupType); } return info; |
