diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2017-12-13 15:48:29 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-12-13 15:48:29 -0800 |
| commit | 6d6142122b15461d6c8cabdb31292b0de688ba35 (patch) | |
| tree | e33d1d6f68fc5a37372c89b31edb059ab6373ec8 /source | |
| parent | 0f55649cc1aa8ad3218b7f8ba7b1eabdd2ec6526 (diff) | |
Fix parameter block binding for Vulkan (#308)
Fixes #307
This ends up being a major overhaul over how type layout computation is structured and exposed.
The big problems all arise around cases where both the "container" for a parameter block or CB, and the "element" type both use the same kind of resource.
E.g., if you define a CB with a texture in it, then in Vulkan both the CB and the texture use the same kind of resource, and so if you query the CB's resource usage it will just tell you it uses two descriptor-table slots, but nothing more than that.
Similar confusion still arises in the HLSL case, when a CB with a texture in it reports its parameter category as "mixed" so that a user might query for a category they didn't mean to. There were also cases in the existing code where a parameter block might expose *both* a register-space usage and another concrete resource type, which isn't right.
The most important changes here are:
- A `ParameterGroupTypeLayout` now has a more refined internal structure, consisting of:
- A `containerTypeLayout`, which represents the resource usage of the buffer/block itself (e.g., if a constant buffer had to be allocated)
- An `elementVarLayout` which stores the offsets that need to be applied to get from the `VarLayout` for an instance of this parameter-group type to the offsets of its elements. The `TypeLayout` for this variable layout should be the "raw" type of the block/CB element.
- The `offsetElementTypeLayout` (formerly just `elementTypeLayout`) which represents the element type, but in the case of a `struct` element type, will have fields offset similar to the `elementVarLayout`. This is what all the old code used to use, so we need to keep it for compatibility.
- When doing reflection on a `ParameterGroupTypeLayout`, we now only report the resource usage of the `containerTypeLayout`. This is technically a potentially breaking change in the public API, but I don't think Falcor will mind, since they actually want something closer to this behavior.
- Add a new public API for querying the element variable layout of a parameter block of constant buffer. This could be used by savvy applications to fold the handling of CB element offsetting into some notion of a "reflection path." This would be required for applications that want to handle CBs or parameter blocks where the element type is *not* a `struct` type.
- Remove old logic for applying an offset when creating a type layout for constant buffer element, and instead perform offsetting more uniformly later, by constructing the `offsetElementTypeLayout` from the `rawElementTypeLayout`. This is useful both because we want to keep both (the "raw" type layout becomes the type layout of the `elementVarLayout`), and also because we can decide later whether we even want to allocate a CB register for a buffer, based on whether it actually contains any uniform data.
- Fix cases where we might end up with a parameter block type reporting both that it uses a whole register space (and thus should not expose the resource usage of the container/element type) *and* a constant-buffer register/slot. The latter should be hidden inside the regsiter space.
- Clean up the `spReflectionParameter_GetBinding{Index,Space}` functions to just route to `spReflectionVariableLayout_Get{Offset,Space}`, using the "default" category of the parameter
- Try to make the `GetSpace` query take into account cases where a variable also has an explicit `RegisterSpace` allocation.
- This probably still needs some cleanup, since ideally we'd just move things into the `space` field on the `ReosurceInfo` and have an invariant that a variable *either* has a `RegisterSpace` allocation, or it has other resource infos, but never both...
- Add some ad-hoc logic so that if the user queries for a binding index/space using a parameter category that doesn't actually apply (e.g., they query for a D3D `t` register when using Vulkan), we can optionally remap it to the resource type they "probably" meant. This is a mess of Do What I Mean code, but it is also what our users want right now.
- Fix various bits of emit logic so that if a parameter block has a register space/set allocated to it, we properly output that as part of the binding information for it.
- This is another thing that might be cleaned up if we rationale the way that things get split during legalization.
- Add a GLSL case for emitting a parameter block variable as a `cbuffer`.
Diffstat (limited to 'source')
| -rw-r--r-- | source/slang/ast-legalize.cpp | 2 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 100 | ||||
| -rw-r--r-- | source/slang/legalize-types.cpp | 49 | ||||
| -rw-r--r-- | source/slang/reflection.cpp | 142 | ||||
| -rw-r--r-- | source/slang/type-layout.cpp | 294 | ||||
| -rw-r--r-- | source/slang/type-layout.h | 19 |
6 files changed, 435 insertions, 171 deletions
diff --git a/source/slang/ast-legalize.cpp b/source/slang/ast-legalize.cpp index 0c9d6154e..144277301 100644 --- a/source/slang/ast-legalize.cpp +++ b/source/slang/ast-legalize.cpp @@ -3129,7 +3129,7 @@ struct LoweringVisitor while (auto parameterGroupTypeLayout = typeLayout.As<ParameterGroupTypeLayout>()) { - typeLayout = parameterGroupTypeLayout->elementTypeLayout; + typeLayout = parameterGroupTypeLayout->offsetElementTypeLayout; } while (auto arrayTypeLayout = typeLayout.As<ArrayTypeLayout>()) diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 09d209f6e..33fd46bdb 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -3454,10 +3454,13 @@ struct EmitVisitor // Emit a single `regsiter` semantic, as appropriate for a given resource-type-specific layout info void emitHLSLRegisterSemantic( VarLayout::ResourceInfo const& info, + UInt spaceOffset, // Keyword to use in the uniform case (`register` for globals, `packoffset` inside a `cbuffer`) char const* uniformSemanticSpelling = "register") { + UInt space = info.space + spaceOffset; + switch(info.kind) { case LayoutResourceKind::Uniform: @@ -3529,10 +3532,10 @@ struct EmitVisitor break; } Emit(info.index); - if(info.space) + if(space) { Emit(", space"); - Emit(info.space); + Emit(space); } Emit(")"); } @@ -3557,7 +3560,7 @@ struct EmitVisitor for( auto rr : layout->resourceInfos ) { - emitHLSLRegisterSemantic(rr, uniformSemanticSpelling); + emitHLSLRegisterSemantic(rr, getSpaceOffset(layout), uniformSemanticSpelling); } } @@ -3590,6 +3593,7 @@ struct EmitVisitor auto offsetResource = rr; + UInt spaceOffset = 0; if(layout && kind != LayoutResourceKind::Uniform) { @@ -3607,9 +3611,11 @@ struct EmitVisitor offsetResource.index += cbufferResource->index; offsetResource.space += cbufferResource->space; } + + spaceOffset = getSpaceOffset(layout); } - emitHLSLRegisterSemantic(offsetResource, "packoffset"); + emitHLSLRegisterSemantic(offsetResource, spaceOffset, "packoffset"); } } @@ -3634,7 +3640,7 @@ struct EmitVisitor auto info = layout->FindResourceInfo(LayoutResourceKind::ConstantBuffer); SLANG_RELEASE_ASSERT(info); - emitHLSLRegisterSemantic(*info); + emitHLSLRegisterSemantic(*info, getSpaceOffset(layout)); Emit("\n{\n"); @@ -3648,7 +3654,7 @@ struct EmitVisitor // RefPtr<Type> elementType = parameterBlockType->elementType; - RefPtr<TypeLayout> elementTypeLayout = bufferLayout->elementTypeLayout; + RefPtr<TypeLayout> elementTypeLayout = bufferLayout->offsetElementTypeLayout; EmitType(elementType, varDecl->getName()); @@ -3690,7 +3696,7 @@ struct EmitVisitor RefPtr<ParameterGroupTypeLayout> bufferLayout = layout->typeLayout.As<ParameterGroupTypeLayout>(); SLANG_RELEASE_ASSERT(bufferLayout); - RefPtr<StructTypeLayout> structTypeLayout = bufferLayout->elementTypeLayout.As<StructTypeLayout>(); + RefPtr<StructTypeLayout> structTypeLayout = bufferLayout->offsetElementTypeLayout.As<StructTypeLayout>(); SLANG_RELEASE_ASSERT(structTypeLayout); @@ -3708,7 +3714,7 @@ struct EmitVisitor auto info = layout->FindResourceInfo(LayoutResourceKind::ConstantBuffer); SLANG_RELEASE_ASSERT(info); - emitHLSLRegisterSemantic(*info); + emitHLSLRegisterSemantic(*info, getSpaceOffset(layout)); Emit("\n{\n"); @@ -3757,8 +3763,10 @@ struct EmitVisitor } void emitGLSLLayoutQualifier( - VarLayout::ResourceInfo const& info) + VarLayout::ResourceInfo const& info, + UInt spaceOffset) { + UInt space = info.space + spaceOffset; switch(info.kind) { case LayoutResourceKind::Uniform: @@ -3812,10 +3820,10 @@ struct EmitVisitor case LayoutResourceKind::DescriptorTableSlot: Emit("layout(binding = "); Emit(info.index); - if(info.space) + if(space) { Emit(", set = "); - Emit(info.space); + Emit(space); } Emit(")\n"); break; @@ -3827,6 +3835,13 @@ struct EmitVisitor } } + UInt getSpaceOffset(VarLayout* layout) + { + if (auto resInfo = layout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) + return resInfo->index; + return 0; + } + void emitGLSLLayoutQualifiers( RefPtr<VarLayout> layout, LayoutResourceKind filter = LayoutResourceKind::None) @@ -3842,6 +3857,8 @@ struct EmitVisitor break; } + UInt spaceOffset = getSpaceOffset(layout); + for( auto info : layout->resourceInfos ) { // Skip info that doesn't match our filter @@ -3851,15 +3868,46 @@ struct EmitVisitor continue; } - emitGLSLLayoutQualifier(info); + emitGLSLLayoutQualifier(info, spaceOffset); } } + void emitGLSLParameterBlockDecl( + RefPtr<VarDeclBase> varDecl, + RefPtr<ParameterBlockType> parameterBlockType, + RefPtr<VarLayout> layout) + { + EmitModifiers(varDecl); + emitGLSLLayoutQualifiers(layout); + Emit("uniform "); + + emitName(varDecl); + + Emit("\n{\n"); + + RefPtr<ParameterGroupTypeLayout> bufferLayout = layout->typeLayout.As<ParameterGroupTypeLayout>(); + SLANG_RELEASE_ASSERT(bufferLayout); + + RefPtr<Type> elementType = parameterBlockType->elementType; + RefPtr<TypeLayout> elementTypeLayout = bufferLayout->offsetElementTypeLayout; + + EmitType(elementType, varDecl->getName()); + Emit(";\n"); + + Emit("};\n"); + } + void emitGLSLParameterGroupDecl( RefPtr<VarDeclBase> varDecl, RefPtr<ParameterGroupType> parameterGroupType, RefPtr<VarLayout> layout) { + if( auto parameterBlockType = parameterGroupType->As<ParameterBlockType>()) + { + emitGLSLParameterBlockDecl(varDecl, parameterBlockType, layout); + return; + } + // The data type that describes where stuff in the constant buffer should go RefPtr<Type> dataType = parameterGroupType->elementType; @@ -3871,7 +3919,7 @@ struct EmitVisitor auto typeLayout = layout->typeLayout; if (auto bufferLayout = typeLayout.As<ParameterGroupTypeLayout>()) { - typeLayout = bufferLayout->elementTypeLayout; + typeLayout = bufferLayout->offsetElementTypeLayout; } structTypeLayout = typeLayout.As<StructTypeLayout>(); @@ -3884,7 +3932,13 @@ struct EmitVisitor EmitModifiers(varDecl); // Emit an apprpriate declaration keyword based on the kind of block - if (parameterGroupType->As<ConstantBufferType>()) + if (parameterGroupType->As<GLSLShaderStorageBufferType>()) + { + Emit("buffer"); + } + // Note: tested `buffer` case before `uniform`, since `GLSLShaderStorageBufferType` + // is also a subclass of `UniformParameterGroupType`. + else if (parameterGroupType->As<UniformParameterGroupType>()) { Emit("uniform"); } @@ -3896,10 +3950,6 @@ struct EmitVisitor { Emit("out"); } - else if (parameterGroupType->As<GLSLShaderStorageBufferType>()) - { - Emit("buffer"); - } else { SLANG_DIAGNOSE_UNEXPECTED(getSink(), varDecl, "unhandled GLSL shader parameter kind"); @@ -6391,7 +6441,7 @@ emitDeclImpl(decl, nullptr); auto info = layout->FindResourceInfo(LayoutResourceKind::ConstantBuffer); SLANG_RELEASE_ASSERT(info); - emitHLSLRegisterSemantic(*info); + emitHLSLRegisterSemantic(*info, getSpaceOffset(layout)); emit("\n{\n"); @@ -6400,7 +6450,7 @@ emitDeclImpl(decl, nullptr); auto typeLayout = layout->typeLayout; if( auto parameterGroupTypeLayout = typeLayout.As<ParameterGroupTypeLayout>() ) { - typeLayout = parameterGroupTypeLayout->elementTypeLayout; + typeLayout = parameterGroupTypeLayout->offsetElementTypeLayout; } emitIRType(ctx, elementType, getIRName(varDecl)); @@ -6430,7 +6480,7 @@ emitDeclImpl(decl, nullptr); auto info = layout->FindResourceInfo(LayoutResourceKind::ConstantBuffer); SLANG_RELEASE_ASSERT(info); - emitHLSLRegisterSemantic(*info); + emitHLSLRegisterSemantic(*info, getSpaceOffset(layout)); emit("\n{\n"); @@ -6439,7 +6489,7 @@ emitDeclImpl(decl, nullptr); auto typeLayout = layout->typeLayout; if( auto parameterGroupTypeLayout = typeLayout.As<ParameterGroupTypeLayout>() ) { - typeLayout = parameterGroupTypeLayout->elementTypeLayout; + typeLayout = parameterGroupTypeLayout->offsetElementTypeLayout; } if(auto declRefType = elementType->As<DeclRefType>()) @@ -6497,7 +6547,7 @@ emitDeclImpl(decl, nullptr); auto info = layout->FindResourceInfo(LayoutResourceKind::DescriptorTableSlot); if (info) { - emitGLSLLayoutQualifier(*info); + emitGLSLLayoutQualifier(*info, getSpaceOffset(layout)); } if(type->As<GLSLShaderStorageBufferType>()) @@ -6519,7 +6569,7 @@ emitDeclImpl(decl, nullptr); auto typeLayout = layout->typeLayout; if( auto parameterGroupTypeLayout = typeLayout.As<ParameterGroupTypeLayout>() ) { - typeLayout = parameterGroupTypeLayout->elementTypeLayout; + typeLayout = parameterGroupTypeLayout->offsetElementTypeLayout; } if(auto declRefType = elementType->As<DeclRefType>()) @@ -6969,7 +7019,7 @@ StructTypeLayout* getGlobalStructLayout( // and hope that the global-scope block (`$Globals`) gets auto-assigned // the same location that we manually asigned it. - auto elementTypeLayout = globalConstantBufferLayout->elementTypeLayout; + auto elementTypeLayout = globalConstantBufferLayout->offsetElementTypeLayout; auto elementTypeStructLayout = elementTypeLayout.As<StructTypeLayout>(); // We expect all constant buffers to contain `struct` types for now diff --git a/source/slang/legalize-types.cpp b/source/slang/legalize-types.cpp index fb9c70c97..1264201a2 100644 --- a/source/slang/legalize-types.cpp +++ b/source/slang/legalize-types.cpp @@ -972,7 +972,7 @@ RefPtr<TypeLayout> getDerefTypeLayout( if (auto parameterGroupTypeLayout = dynamic_cast<ParameterGroupTypeLayout*>(typeLayout)) { - return parameterGroupTypeLayout->elementTypeLayout; + return parameterGroupTypeLayout->offsetElementTypeLayout; } return typeLayout; @@ -993,7 +993,7 @@ RefPtr<VarLayout> getFieldLayout( } else if(auto parameterGroupTypeLayotu = dynamic_cast<ParameterGroupTypeLayout*>(typeLayout)) { - typeLayout = parameterGroupTypeLayotu->elementTypeLayout; + typeLayout = parameterGroupTypeLayotu->offsetElementTypeLayout; } else { @@ -1029,6 +1029,19 @@ RefPtr<VarLayout> createVarLayout( RefPtr<VarLayout> varLayout = new VarLayout(); varLayout->typeLayout = typeLayout; + // For most resource kinds, the register index/space to use should + // be the sum along the entire chain of variables. + // + // For example, if we had input: + // + // struct S { Texture2D a; Texture2D b; }; + // S s : register(t10); + // + // And we were generating a stand-alone variable for `s.b`, then + // we'd need to add the offset for `b` (1 texture register), to + // the offset for `s` (10 texture registers) to get the final + // binding to apply. + // for (auto rr : typeLayout->resourceInfos) { auto resInfo = varLayout->findOrAddResourceInfo(rr.kind); @@ -1043,28 +1056,30 @@ RefPtr<VarLayout> createVarLayout( } } - // Some of the parent variables might actually contain offsets - // to the `space` or `set` of the field, and we need to apply - // those to all the nested resource infos. - for (auto vv = varChain; vv; vv = vv->next) + // As a special case, if the leaf variable doesn't hold an entry for + // `RegisterSpace`, but at least one declaration in the chain *does*, + // then we want to make sure that we add such an entry. + if (!varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) { - auto parentSpaceInfo = vv->varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace); - if (!parentSpaceInfo) - continue; - - for (auto& rr : varLayout->resourceInfos) + // Sum up contributions from all parents. + UInt space = 0; + for (auto vv = varChain; vv; vv = vv->next) { - if (rr.kind == LayoutResourceKind::RegisterSpace) - { - rr.index += parentSpaceInfo->index; - } - else + if (auto parentResInfo = vv->varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) { - rr.space += parentSpaceInfo->index; + space += parentResInfo->index; } } + + // If there were non-zero contributions, then add an entry to represent them. + if (space) + { + varLayout->findOrAddResourceInfo(LayoutResourceKind::RegisterSpace)->index = space; + } } + + return varLayout; } diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp index 5962bce96..7068ecea4 100644 --- a/source/slang/reflection.cpp +++ b/source/slang/reflection.cpp @@ -529,7 +529,7 @@ SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_GetElementTypeLayout } else if( auto constantBufferTypeLayout = dynamic_cast<ParameterGroupTypeLayout*>(typeLayout)) { - return convert(constantBufferTypeLayout->elementTypeLayout.Ptr()); + return convert(constantBufferTypeLayout->offsetElementTypeLayout.Ptr()); } else if( auto structuredBufferTypeLayout = dynamic_cast<StructuredBufferTypeLayout*>(typeLayout)) { @@ -539,6 +539,19 @@ SLANG_API SlangReflectionTypeLayout* spReflectionTypeLayout_GetElementTypeLayout return nullptr; } +SLANG_API SlangReflectionVariableLayout* spReflectionTypeLayout_GetElementVarLayout(SlangReflectionTypeLayout* inTypeLayout) +{ + auto typeLayout = convert(inTypeLayout); + if(!typeLayout) return nullptr; + + if( auto constantBufferTypeLayout = dynamic_cast<ParameterGroupTypeLayout*>(typeLayout)) + { + return convert(constantBufferTypeLayout->elementVarLayout.Ptr()); + } + + return nullptr; +} + static SlangParameterCategory getParameterCategory( LayoutResourceKind kind) { @@ -566,6 +579,11 @@ SLANG_API SlangParameterCategory spReflectionTypeLayout_GetParameterCategory(Sla auto typeLayout = convert(inTypeLayout); if(!typeLayout) return SLANG_PARAMETER_CATEGORY_NONE; + if (auto parameterGroupTypeLayout = dynamic_cast<ParameterGroupTypeLayout*>(typeLayout)) + { + typeLayout = parameterGroupTypeLayout->containerTypeLayout; + } + return getParameterCategory(typeLayout); } @@ -574,6 +592,11 @@ SLANG_API unsigned spReflectionTypeLayout_GetCategoryCount(SlangReflectionTypeLa auto typeLayout = convert(inTypeLayout); if(!typeLayout) return 0; + if (auto parameterGroupTypeLayout = dynamic_cast<ParameterGroupTypeLayout*>(typeLayout)) + { + typeLayout = parameterGroupTypeLayout->containerTypeLayout; + } + return (unsigned) typeLayout->resourceInfos.Count(); } @@ -582,6 +605,11 @@ SLANG_API SlangParameterCategory spReflectionTypeLayout_GetCategoryByIndex(Slang auto typeLayout = convert(inTypeLayout); if(!typeLayout) return SLANG_PARAMETER_CATEGORY_NONE; + if (auto parameterGroupTypeLayout = dynamic_cast<ParameterGroupTypeLayout*>(typeLayout)) + { + typeLayout = parameterGroupTypeLayout->containerTypeLayout; + } + return typeLayout->resourceInfos[index].kind; } @@ -626,12 +654,68 @@ SLANG_API SlangReflectionTypeLayout* spReflectionVariableLayout_GetTypeLayout(Sl return convert(varLayout->getTypeLayout()); } +namespace Slang +{ + // Attempt "do what I mean" remapping from the parameter category the user asked about, + // over to a parameter category that they might have meant. + static SlangParameterCategory maybeRemapParameterCategory( + TypeLayout* typeLayout, + SlangParameterCategory category) + { + // Do we have an entry for the category they asked about? Then use that. + if (typeLayout->FindResourceInfo(LayoutResourceKind(category))) + return category; + + // Do we have an entry for the `DescriptorTableSlot` category? + if (typeLayout->FindResourceInfo(LayoutResourceKind::DescriptorTableSlot)) + { + // Is the category they were asking about one that makes sense for the type + // of this variable? + Type* type = typeLayout->getType(); + while (auto arrayType = type->As<ArrayExpressionType>()) + type = arrayType->baseType; + switch (spReflectionType_GetKind(convert(type))) + { + case SLANG_TYPE_KIND_CONSTANT_BUFFER: + if(category == SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER) + return SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT; + break; + + case SLANG_TYPE_KIND_RESOURCE: + if(category == SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE) + return SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT; + break; + + case SLANG_TYPE_KIND_SAMPLER_STATE: + if(category == SLANG_PARAMETER_CATEGORY_SAMPLER_STATE) + return SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT; + break; + + // TODO: implement more helpers here + + default: + break; + } + } + + return category; + } +} + SLANG_API size_t spReflectionVariableLayout_GetOffset(SlangReflectionVariableLayout* inVarLayout, SlangParameterCategory category) { auto varLayout = convert(inVarLayout); if(!varLayout) return 0; auto info = varLayout->FindResourceInfo(LayoutResourceKind(category)); + + if (!info) + { + // No match with requested category? Try again with one they might have meant... + category = maybeRemapParameterCategory(varLayout->getTypeLayout(), category); + info = varLayout->FindResourceInfo(LayoutResourceKind(category)); + } + if(!info) return 0; return info->index; @@ -642,10 +726,30 @@ SLANG_API size_t spReflectionVariableLayout_GetSpace(SlangReflectionVariableLayo auto varLayout = convert(inVarLayout); if(!varLayout) return 0; + auto info = varLayout->FindResourceInfo(LayoutResourceKind(category)); - if(!info) return 0; + if (!info) + { + // No match with requested category? Try again with one they might have meant... + category = maybeRemapParameterCategory(varLayout->getTypeLayout(), category); + info = varLayout->FindResourceInfo(LayoutResourceKind(category)); + } + + UInt space = 0; + + // First, deal with any offset applied to the specific resource kind specified + if (info) + { + space += info->space; + } + + // Next, deal with any dedicated register-space offset applied to, e.g., a parameter block + if (auto spaceInfo = varLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) + { + space += spaceInfo->index; + } - return info->space; + return space; } SLANG_API char const* spReflectionVariableLayout_GetSemanticName(SlangReflectionVariableLayout* inVarLayout) @@ -705,28 +809,20 @@ SLANG_API SlangStage spReflectionVariableLayout_getStage( SLANG_API unsigned spReflectionParameter_GetBindingIndex(SlangReflectionParameter* inVarLayout) { - auto varLayout = convert(inVarLayout); - if(!varLayout) return 0; - - if(varLayout->resourceInfos.Count() > 0) - { - return (unsigned) varLayout->resourceInfos[0].index; - } - - return 0; + SlangReflectionVariableLayout* varLayout = (SlangReflectionVariableLayout*)inVarLayout; + return (unsigned) spReflectionVariableLayout_GetOffset( + varLayout, + spReflectionTypeLayout_GetParameterCategory( + spReflectionVariableLayout_GetTypeLayout(varLayout))); } SLANG_API unsigned spReflectionParameter_GetBindingSpace(SlangReflectionParameter* inVarLayout) { - auto varLayout = convert(inVarLayout); - if(!varLayout) return 0; - - if(varLayout->resourceInfos.Count() > 0) - { - return (unsigned) varLayout->resourceInfos[0].space; - } - - return 0; + SlangReflectionVariableLayout* varLayout = (SlangReflectionVariableLayout*)inVarLayout; + return (unsigned) spReflectionVariableLayout_GetSpace( + varLayout, + spReflectionTypeLayout_GetParameterCategory( + spReflectionVariableLayout_GetTypeLayout(varLayout))); } // Helpers for getting parameter count @@ -737,7 +833,7 @@ namespace Slang { if(auto parameterGroupLayout = typeLayout.As<ParameterGroupTypeLayout>()) { - typeLayout = parameterGroupLayout->elementTypeLayout; + typeLayout = parameterGroupLayout->offsetElementTypeLayout; } if(auto structLayout = typeLayout.As<StructTypeLayout>()) @@ -752,7 +848,7 @@ namespace Slang { if(auto parameterGroupLayout = typeLayout.As<ParameterGroupTypeLayout>()) { - typeLayout = parameterGroupLayout->elementTypeLayout; + typeLayout = parameterGroupLayout->offsetElementTypeLayout; } if(auto structLayout = typeLayout.As<StructTypeLayout>()) diff --git a/source/slang/type-layout.cpp b/source/slang/type-layout.cpp index 30b2ee01a..da1337778 100644 --- a/source/slang/type-layout.cpp +++ b/source/slang/type-layout.cpp @@ -758,8 +758,7 @@ static SimpleLayoutInfo getParameterGroupLayoutInfo( RefPtr<TypeLayout> createTypeLayout( TypeLayoutContext const& context, - Type* type, - SimpleLayoutInfo offset); + Type* type); static bool isOpenGLTarget(TargetRequest*) { @@ -860,21 +859,110 @@ static bool shouldAllocateRegisterSpaceForParameterBlock( return false; } +// Given an existing type layout `oldTypeLayout`, apply offsets +// to any contained fields based on the resource infos in `offsetTypeLayout` +// *and* the usage implied by `offsetLayout` +RefPtr<TypeLayout> applyOffsetToTypeLayout( + RefPtr<TypeLayout> oldTypeLayout, + RefPtr<TypeLayout> offsetTypeLayout) +{ + // There is no need to apply offsets if the old type and the offset + // don't share any resource infos in common. + bool anyHit = false; + for (auto oldResInfo : oldTypeLayout->resourceInfos) + { + if (auto offsetResInfo = offsetTypeLayout->FindResourceInfo(oldResInfo.kind)) + { + anyHit = true; + break; + } + } + + if (!anyHit) + return oldTypeLayout; + + RefPtr<TypeLayout> newTypeLayout; + if (auto oldStructTypeLayout = oldTypeLayout.As<StructTypeLayout>()) + { + RefPtr<StructTypeLayout> newStructTypeLayout = new StructTypeLayout(); + newStructTypeLayout->type = oldStructTypeLayout->type; + newStructTypeLayout->uniformAlignment = oldStructTypeLayout->uniformAlignment; + + Dictionary<VarLayout*, VarLayout*> mapOldFieldToNew; + + for (auto oldField : oldStructTypeLayout->fields) + { + RefPtr<VarLayout> newField = new VarLayout(); + newField->varDecl = oldField->varDecl; + newField->typeLayout = oldField->typeLayout; + newField->flags = oldField->flags; + newField->semanticIndex = oldField->semanticIndex; + newField->semanticName = oldField->semanticName; + newField->stage = oldField->stage; + newField->systemValueSemantic = oldField->systemValueSemantic; + newField->systemValueSemanticIndex = oldField->systemValueSemanticIndex; + + + for (auto oldResInfo : oldField->resourceInfos) + { + auto newResInfo = newField->findOrAddResourceInfo(oldResInfo.kind); + newResInfo->index = oldResInfo.index; + newResInfo->space = oldResInfo.space; + if (auto offsetResInfo = offsetTypeLayout->FindResourceInfo(oldResInfo.kind)) + { + newResInfo->index += offsetResInfo->count; + } + } + + newStructTypeLayout->fields.Add(newField); + + mapOldFieldToNew.Add(oldField.Ptr(), newField.Ptr()); + } + + for (auto entry : oldStructTypeLayout->mapVarToLayout) + { + VarLayout* newFieldLayout = nullptr; + if (mapOldFieldToNew.TryGetValue(entry.Value.Ptr(), newFieldLayout)) + { + newStructTypeLayout->mapVarToLayout.Add(entry.Key, newFieldLayout); + } + } + + newTypeLayout = newStructTypeLayout; + } + else + { + // TODO: need to handle other cases here + return oldTypeLayout; + } + + // No matter what replacement we plug in for the element type, we need to copy + // over its resource usage: + for (auto oldResInfo : oldTypeLayout->resourceInfos) + { + auto newResInfo = newTypeLayout->findOrAddResourceInfo(oldResInfo.kind); + newResInfo->count = oldResInfo.count; + } + + return newTypeLayout; +} + RefPtr<ParameterGroupTypeLayout> createParameterGroupTypeLayout( TypeLayoutContext const& context, RefPtr<ParameterGroupType> parameterGroupType, SimpleLayoutInfo parameterGroupInfo, - RefPtr<TypeLayout> elementTypeLayout) + RefPtr<TypeLayout> rawElementTypeLayout) { auto parameterGroupRules = context.rules; - auto typeLayout = new ParameterGroupTypeLayout(); - + RefPtr<ParameterGroupTypeLayout> typeLayout = new ParameterGroupTypeLayout(); typeLayout->type = parameterGroupType; typeLayout->rules = parameterGroupRules; - typeLayout->elementTypeLayout = elementTypeLayout; + RefPtr<TypeLayout> containerTypeLayout = new TypeLayout(); + containerTypeLayout->type = parameterGroupType; + containerTypeLayout->rules = parameterGroupRules; // The layout of the constant buffer if it gets stored // in another constant buffer is just what we computed @@ -883,6 +971,7 @@ createParameterGroupTypeLayout( // SLANG_RELEASE_ASSERT(parameterGroupInfo.kind != LayoutResourceKind::Uniform); typeLayout->uniformAlignment = 1; + containerTypeLayout->uniformAlignment = 1; // TODO(tfoley): There is a subtle question here of whether // a constant buffer declaration that then contains zero @@ -896,7 +985,7 @@ createParameterGroupTypeLayout( // parameter block itself. if( parameterGroupInfo.size ) { - typeLayout->addResourceUsage( + containerTypeLayout->addResourceUsage( parameterGroupInfo.kind, parameterGroupInfo.size); } @@ -916,35 +1005,22 @@ createParameterGroupTypeLayout( bool ownRegisterSpace = false; if (parameterBlockType) { + // Should we allocate this block its own regsiter space? if( shouldAllocateRegisterSpaceForParameterBlock(context) ) { ownRegisterSpace = true; } - // If the parameter block contains any uniform data, then we - // had better allocate a constant buffer for it. - bool anyUniformData = false; - if(auto elementUniformInfo = elementTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform) ) - { - if( elementUniformInfo->count != 0 ) - { - // We have a non-zero number of bytes of uniform data here. - anyUniformData = true; - } - } - - if( anyUniformData ) - { - typeLayout->addResourceUsage(LayoutResourceKind::ConstantBuffer, 1); - } - - // Next, if we are going to allocate whole register space(s) to the - // parameter block, check if it actually needs one (it might be empty, - // or only contain other parameter blocks). + // If we need a register space, then maybe allocate one. if( ownRegisterSpace ) { + // The basic logic here is that if the parameter block only + // contains other parameter blocks (which themselves have + // their own register spaces), then we don't need to + // allocate *yet another* register space for the block. + bool needsARegisterSpace = false; - for( auto elementResourceInfo : elementTypeLayout->resourceInfos ) + for( auto elementResourceInfo : rawElementTypeLayout->resourceInfos ) { if(elementResourceInfo.kind != LayoutResourceKind::RegisterSpace) { @@ -953,11 +1029,31 @@ createParameterGroupTypeLayout( } } + // If we determine that a register space is needed, then add one here. if( needsARegisterSpace ) { typeLayout->addResourceUsage(LayoutResourceKind::RegisterSpace, 1); } } + + // Next, we check if the parameter block has any uniform data, since + // that means we need to allocate a constant-buffer binding for it. + bool anyUniformData = false; + if(auto elementUniformInfo = rawElementTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform) ) + { + if( elementUniformInfo->count != 0 ) + { + // We have a non-zero number of bytes of uniform data here. + anyUniformData = true; + } + } + if( anyUniformData ) + { + // We need to ensure that the block itself consumes at least one "register" for its + // constant buffer part. + auto cbUsage = parameterGroupRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer); + containerTypeLayout->addResourceUsage(cbUsage.kind, cbUsage.size); + } } // The layout for the element type was computed without any knowledge @@ -965,45 +1061,73 @@ createParameterGroupTypeLayout( // need to go through and offset that any starting locations (e.g., // in nested `StructTypeLayout`s) based on what we allocated to // the parent. + // + // Note: at the moment, constant buffers apply their own offsetting + // logic elsewhere, so we need to only do this logic for parameter blocks + RefPtr<TypeLayout> offsetTypeLayout = applyOffsetToTypeLayout(rawElementTypeLayout, containerTypeLayout); + + typeLayout->containerTypeLayout = containerTypeLayout; + typeLayout->offsetElementTypeLayout = offsetTypeLayout; + + // We will construct a dummy variable layout to represent the offsettting + // that needs to be applied to the element type to put it after the + // container. + RefPtr<VarLayout> elementVarLayout = new VarLayout(); + elementVarLayout->typeLayout = rawElementTypeLayout; + for (auto containerResourceInfo : containerTypeLayout->resourceInfos) + { + auto kind = containerResourceInfo.kind; + if (auto elementResourceInfo = rawElementTypeLayout->FindResourceInfo(kind)) + { + auto varResourceInfo = elementVarLayout->findOrAddResourceInfo(kind); + varResourceInfo->index += containerResourceInfo.count; + } + } + typeLayout->elementVarLayout = elementVarLayout; - // TODO(tfoley): actually implement that! - - - // Now we will (possibly) accumulate the resources used by the element - // type into the resources used by the parameter group. The reason - // this is "possibly" is because, e.g., a `ConstantBuffer<Foo>` should - // not report itself as consuming `sizeof(Foo)` bytes of uniform data, - // or else it would mess up layout for any type that contains the - // constant buffer. Similarly, a parameter block that consumes whole - // register `space`s shouldn't report its fine-grained resource - // usage inside those spces. - for( auto elementResourceInfo : elementTypeLayout->resourceInfos ) + if (ownRegisterSpace) { - switch( elementResourceInfo.kind ) + // A parameter block type that gets its own register space will only + // include resource usage from the element type when it itself consumes + // while register spaces. + if (auto elementResInfo = rawElementTypeLayout->FindResourceInfo(LayoutResourceKind::RegisterSpace)) { - case LayoutResourceKind::RegisterSpace: - // Register spaces consumed by the element type should be - // reflected in the resource usage of the parent type. - typeLayout->addResourceUsage(elementResourceInfo); - break; + typeLayout->addResourceUsage(*elementResInfo); + } + } + else + { + // If the parameter block is *not* getting its own regsiter space, then + // it needs to include the resource usage from the "container" type, plus + // any relevant resource usage for the element type. - case LayoutResourceKind::Uniform: - // Uniform resource usages will always be hidden. - break; + // We start by accumulating any resource usage from the container. + for (auto containerResourceInfo : containerTypeLayout->resourceInfos) + { + typeLayout->addResourceUsage(containerResourceInfo); + } - default: - // All other register types should be hidden *if* we - // are allocating a whole register space, and exposed - // otherwise. - if( ownRegisterSpace ) - { - // don't expose internal register/binding use outside - } - else + // Now we will (possibly) accumulate the resources used by the element + // type into the resources used by the parameter group. The reason + // this is "possibly" is because, e.g., a `ConstantBuffer<Foo>` should + // not report itself as consuming `sizeof(Foo)` bytes of uniform data, + // or else it would mess up layout for any type that contains the + // constant buffer. + for( auto elementResourceInfo : rawElementTypeLayout->resourceInfos ) + { + switch( elementResourceInfo.kind ) { + case LayoutResourceKind::Uniform: + // Uniform resource usages will always be hidden. + break; + + default: + // All other register types will not be hidden, + // since we aren't in the case where the parameter group + // gets its own register space. typeLayout->addResourceUsage(elementResourceInfo); + break; } - break; } } @@ -1059,8 +1183,7 @@ createParameterGroupTypeLayout( auto elementTypeLayout = createTypeLayout( context.with(elementTypeRules), - elementType, - info); + elementType); return createParameterGroupTypeLayout( context, @@ -1188,16 +1311,7 @@ createStructuredBufferTypeLayout( SimpleLayoutInfo GetLayoutImpl( TypeLayoutContext const& context, Type* type, - RefPtr<TypeLayout>* outTypeLayout, - SimpleLayoutInfo offset); - -SimpleLayoutInfo GetLayoutImpl( - TypeLayoutContext const& context, - Type* type, - RefPtr<TypeLayout>* outTypeLayout) -{ - return GetLayoutImpl(context, type, outTypeLayout, SimpleLayoutInfo()); -} + RefPtr<TypeLayout>* outTypeLayout); SimpleLayoutInfo GetLayoutImpl( TypeLayoutContext const& context, @@ -1219,7 +1333,7 @@ SimpleLayoutInfo GetLayoutImpl( // layout, such as GLSL `std140`. } - return GetLayoutImpl(subContext, type, outTypeLayout, SimpleLayoutInfo()); + return GetLayoutImpl(subContext, type, outTypeLayout); } int findGenericParam(List<RefPtr<GenericParamLayout>> & genericParameters, GlobalGenericParamDecl * decl) @@ -1230,8 +1344,7 @@ int findGenericParam(List<RefPtr<GenericParamLayout>> & genericParameters, Globa SimpleLayoutInfo GetLayoutImpl( TypeLayoutContext const& context, Type* type, - RefPtr<TypeLayout>* outTypeLayout, - SimpleLayoutInfo offset) + RefPtr<TypeLayout>* outTypeLayout) { auto rules = context.rules; @@ -1583,15 +1696,6 @@ SimpleLayoutInfo GetLayoutImpl( fieldResourceInfo->index = structTypeResourceInfo->count; structTypeResourceInfo->count += fieldTypeResourceInfo.count; } - - // If the user passed in offset info, then apply it here - if (offset.size) - { - if (auto fieldResInfo = fieldLayout->FindResourceInfo(offset.kind)) - { - fieldResInfo->index += offset.size; - } - } } } @@ -1657,40 +1761,22 @@ SimpleLayoutInfo GetLayout( RefPtr<TypeLayout> createTypeLayout( TypeLayoutContext const& context, - Type* type, - SimpleLayoutInfo offset) -{ - RefPtr<TypeLayout> typeLayout; - GetLayoutImpl(context, type, &typeLayout, offset); - return typeLayout; -} - -RefPtr<TypeLayout> createTypeLayout( - TypeLayoutContext const& context, Type* type) { RefPtr<TypeLayout> typeLayout; - GetLayoutImpl(context, type, &typeLayout, SimpleLayoutInfo()); + GetLayoutImpl(context, type, &typeLayout); return typeLayout; } RefPtr<TypeLayout> CreateTypeLayout( TypeLayoutContext const& context, - Type* type, - SimpleLayoutInfo offset) + Type* type) { RefPtr<TypeLayout> typeLayout; - GetLayoutImpl(context, type, &typeLayout, offset); + GetLayoutImpl(context, type, &typeLayout); return typeLayout; } -RefPtr<TypeLayout> CreateTypeLayout( - TypeLayoutContext const& context, - Type* type) -{ - return CreateTypeLayout(context, type, SimpleLayoutInfo()); -} - RefPtr<GlobalGenericParamDecl> GenericParamTypeLayout::getGlobalGenericParamDecl() { auto declRefType = type->AsDeclRefType(); diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h index 66763c8a7..fc2007437 100644 --- a/source/slang/type-layout.h +++ b/source/slang/type-layout.h @@ -307,7 +307,24 @@ public: class ParameterGroupTypeLayout : public TypeLayout { public: - RefPtr<TypeLayout> elementTypeLayout; + // The layout of the "container" type itself. + // E.g., for a constant buffer, this would reflect + // the resource usage of the container, without + // the element type factored in. + RefPtr<TypeLayout> containerTypeLayout; + + // A variable layout for the element of the container. + // The offsets of the variable layout will reflect + // the offsets that need to applied to get past the + // container types resource usage, while the actual + // type layout won't have offsets applied (unlike + // `offsetElementTypeLayout` below). + RefPtr<VarLayout> elementVarLayout; + + // The layout of the element type, with offsets applied + // so that any fields (if the element type is a `struct`) + // will be offset by the resource usage of the container. + RefPtr<TypeLayout> offsetElementTypeLayout; }; // type layout for a variable that has a constant-buffer type |
