summaryrefslogtreecommitdiffstats
path: root/source
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-01-31 11:33:57 -0800
committerGitHub <noreply@github.com>2019-01-31 11:33:57 -0800
commitf20c64c348393602ed2a9c873386345cc4b493e8 (patch)
treec19eef6102dd841b584ff021b08e5ffdbcbb853f /source
parent11c547d1e94fa620f527c3590174e6e25ab21883 (diff)
Initial support for uniform parameters on entry points (#815)
* Initial support for uniform parameters on entry points The basic feature this work adds is the ability to define a shader entry point like: ```hlsl [shader("fragment")] float4 main( uniform Texture2D t, uniform SamplerState s, float2 uv : UV) { return t.Sample(s,uv); } ``` In this example, the `uniform` keyword is used to mark that the given entry point parameters are *not* varying input/output flowing through the pipeline, but rather uniform shader parameters that should function as if the shader was declared more like: ```hlsl Texture2D t, SamplerState s, [shader("fragment")] float4 main( float2 uv : UV) { return t.Sample(s,uv); } ``` Allowing `uniform` parameters on entry points makes it easier to define multiple entry points in one file without accidentally polluting the global scope with shader parameters that only certain entry points care about. This feature is also more or less a prerequisite for allowing generic type parameters directly on entry point functions, since the main use case for those type parameters is for determining what goes in various `ConstantBuffer`s or `ParameterBlock`s. There are two main pieces to the implementation. First, we need to be able to compute appropriate layout information for entry points that include `uniform` parameters. Second, we need to transform the entry point function to move any `uniform` parameters to be ordinary global-scope shader parameters, to make sure that all other back-end passes don't need to worry about this special case. The latter piece of the implementation is, relatively speaking, simpler. The pass in `ir-entry-point-uniforms.{h,cpp}` converts entry point parameters that are determined to be uniform (using the already-computed layout information) into fields of a `struct` type and then declares a global shader parameter based on that `struct` type (and applies already-computed layout information to that parameter). After that, the remaining IR passes (notably including type legalization) will handle things just as for any other global shader parameter. The changes to the layout step are more significant, but most of the changes are just cleanups and fixes to enable the feature. The two major changes that enable entry-point `uniform` parameters are: * In `collectEntryPointParameters` we now dispatch out to a new `computeEntryPointParameterTypeLayout` function, which decided whether to compute the type layout for a `uniform` parameter, or for a varying parameter (what used to be the default behavior handled by `processEntryPointParameterDecl`). * The main `generateParameterBindings` routine was extended so that it allocates registers/bindings to the resources required by each entry point (using `completeBindingsForParameter`) after it has allocated registers/binding to all of the global-scope parameters (this addition is mirrored in `specializeProgramLayout`). The effect of these changes is that the `uniform` parameters of any entry points specified in a compile request will be laid out after the global-scope parameters, in the order the entry points were specified in the compile request. A bunch of smaller changes were made around parameter layout that are worth enumerating so that the diffs make some sense: * The `EntryPointLayout` type was changed so that instead of trying to *be* a `StructTypeLayout`, it instead *owns* one, in the same fashion as `ProgramLayout`. This commonality was factored into a base class `ScopeLayout`, and a bunch of edits followed from that change. * Because `uniform` parameters are moved out of the entry point parameter list early in the IR transformations, the logic in `ir-glsl-legalize.cpp` that tried to look up parameter layout information by index would no longer work if the entry point parameter list had been altered. Instead, that logic now looks for the decorations directly on the parameters. * The `UsedRange` type in `parameter-binding.cpp` was tracking the existing parameter associated with a range using a `ParameterInfo*` (which accounts for the possibility of multiple `VarDecl`s mapping to the same logical shader parameter), when just using a `VarLayout*` is sufficient for all current use cases. The overhead of allocating a `ParameterInfo` seems like overkill for entry-point parameters, where there can't possibly be multiple declarations of the "same" parameter, so avoiding these overheads was a focus when trying to deduplicate code between the global and entry-point parameter cases. * A bunch of parameter binding logic that was specific to GLSL input has been deleted completely. There was no way to even execute this code in the compiler today, and there is pretty much zero chance of us needing (or wanting) to deal with GLSL input in the future. This includes custom `UsedRangeSet`s specific to each translation unit, which were only needed for global-scope `in` and `out` varying declarations in GLSL. * A bunch of functions with `EntryPointParameter` in their names were renamed to use `EntryPointVaryingParameter` to help distinguish that they only apply to the varying case, while entry point `uniform` parameters are handled elsewhere. * The `completeBindingsForParameter` function was re-worked into something that can be used for both global-scope shader parameters (where we have a `ParameterInfo` and possibly explicit bindings) and entry-point parameters (where we expect to have neither). This helps unify the (fairly subtle) logic for how we allocate and assign bindings for resources, constant buffers, parameter blocks, etc. * A small change was made so that the entry-point stage is attached directly to top-level parameters of the entry point, and *not* recursively to every field along the way. This could be a breaking change for some applications, but it makes more logical sense (to me); we'll have to check if this affects Falcor. This change produces different output for several of the reflection tests, but the changes are consistent with no longer attaching stage information to sub-fields of varying `struct`-type parameters. * Because there is a bunch of repeated logic in `parameter-binding.cpp` that has to do with computing a `struct` layout for ordinary/uniform data, I tried to factor that into a single `ScopeLayoutBuilder` type, which handles computing the offsets for any parameters with ordinary data, and then also handles wrapping up the layout in a constant buffer layout if there was any ordinary data at the end. * A similar convenience routine `maybeAllocateConstantBufferBinding` was added because I noticed multiple places in `parameter-binding.cpp` that were trying to allocate a constant buffer binding for global uniforms, and they were wildly inconsistent (and in most cases used logic that would only work for D3D). * The main `generateParameterBindings` routine is significantly shortened by using all of these utilities that were introduced. I tried to comment the places that changed to explain the overall flow correctly. * The `specializeProgramLayout` routine (used to take a `ProgramLayout` from `generateParameterBindings` and specialize it based on knowledge of global generic arguments) had basically been rewritten with more explicit commenting/rationale for what happens in each step. It makes use of the same shared utilities as `generateParameterBindings` and `collectEntryPointParameters`. In terms of testing: * I added a test case to specifically test the new behavior, and in particular I made sure to include a mix of both global and entry-point parameters and also to have entry-point parameters of both ordinary and resource/object types. * I tweaked an existing test for global type parameters to use an entry-point `uniform` parameter instead of a global one, in an effort to migrate it toward being able to use an explicitly generic entry point. * fixups from merge
Diffstat (limited to 'source')
-rw-r--r--source/slang/emit.cpp72
-rw-r--r--source/slang/ir-entry-point-uniforms.cpp423
-rw-r--r--source/slang/ir-entry-point-uniforms.h12
-rw-r--r--source/slang/ir-glsl-legalize.cpp16
-rw-r--r--source/slang/ir-insts.h3
-rw-r--r--source/slang/ir-link.cpp14
-rw-r--r--source/slang/ir.cpp9
-rw-r--r--source/slang/parameter-binding.cpp1213
-rw-r--r--source/slang/reflection.cpp12
-rw-r--r--source/slang/slang.vcxproj2
-rw-r--r--source/slang/slang.vcxproj.filters6
-rw-r--r--source/slang/type-layout.h21
12 files changed, 1212 insertions, 591 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp
index e4662e3c4..224fa3a28 100644
--- a/source/slang/emit.cpp
+++ b/source/slang/emit.cpp
@@ -3,6 +3,7 @@
#include "../core/slang-writer.h"
#include "ir-dce.h"
+#include "ir-entry-point-uniforms.h"
#include "ir-glsl-legalize.h"
#include "ir-insts.h"
#include "ir-link.h"
@@ -6511,41 +6512,28 @@ EntryPointLayout* findEntryPointLayout(
return nullptr;
}
-// Given a layout computed for a whole program, find
-// the corresponding layout to use when looking up
-// variables at the global scope.
-//
-// It might be that the global scope was logically
-// mapped to a constant buffer, so that we need
-// to "unwrap" that declaration to get at the
-// actual struct type inside.
-StructTypeLayout* getGlobalStructLayout(
- ProgramLayout* programLayout)
+ /// Given a layout computed for a scope, get the layout to use when lookup up variables.
+ ///
+ /// A scope (such as the global scope of a program) groups its
+ /// parameters into a pseudo-`struct` type for layout purposes,
+ /// and in some cases that type will in turn be wrapped in a
+ /// `ConstantBuffer` type to indicate that the parameters needed
+ /// an implicit constant buffer to be allocated.
+ ///
+ /// This function "unwraps" the type layout to find the structure
+ /// type layout that must be stored inside.
+ ///
+StructTypeLayout* getScopeStructLayout(
+ ScopeLayout* scopeLayout)
{
- auto globalScopeLayout = programLayout->globalScopeLayout->typeLayout;
- if( auto gs = as<StructTypeLayout>(globalScopeLayout) )
+ auto scopeTypeLayout = scopeLayout->parametersLayout->typeLayout;
+ if( auto structTypeLayout = as<StructTypeLayout>(scopeTypeLayout) )
{
- return gs;
+ return structTypeLayout;
}
- else if( auto globalConstantBufferLayout = as<ParameterGroupTypeLayout>(globalScopeLayout) )
+ else if( auto constantBufferTypeLayout = as<ParameterGroupTypeLayout>(scopeTypeLayout) )
{
- // TODO: the `cbuffer` case really needs to be emitted very
- // carefully, but that is beyond the scope of what a simple rewriter
- // can easily do (without semantic analysis, etc.).
- //
- // The crux of the problem is that we need to collect all the
- // global-scope uniforms (but not declarations that don't involve
- // uniform storage...) and put them in a single `cbuffer` declaration,
- // so that we can give it an explicit location. The fields in that
- // declaration might use various type declarations, so we'd really
- // need to emit all the type declarations first, and that involves
- // some large scale re orderings.
- //
- // For now we will punt and just emit the declarations normally,
- // and hope that the global-scope block (`$Globals`) gets auto-assigned
- // the same location that we manually assigned it.
-
- auto elementTypeLayout = globalConstantBufferLayout->offsetElementTypeLayout;
+ auto elementTypeLayout = constantBufferTypeLayout->offsetElementTypeLayout;
auto elementTypeStructLayout = as<StructTypeLayout>(elementTypeLayout);
// We expect all constant buffers to contain `struct` types for now
@@ -6560,6 +6548,16 @@ StructTypeLayout* getGlobalStructLayout(
}
}
+ /// Given a layout computed for a program, get the layout to use when lookup up variables.
+ ///
+ /// This is just an alias of `getScopeStructLayout`.
+ ///
+StructTypeLayout* getGlobalStructLayout(
+ ProgramLayout* programLayout)
+{
+ return getScopeStructLayout(programLayout);
+}
+
void legalizeTypes(
TypeLegalizationContext* context,
IRModule* module);
@@ -6657,6 +6655,18 @@ String emitEntryPoint(
// un-specialized IR.
dumpIRIfEnabled(compileRequest, irModule);
+ // Now that we've linked the IR code, any layout/binding
+ // information has been attached to shader parameters
+ // and entry points. Now we are safe to make transformations
+ // that might move code without worrying about losing
+ // the connection between a parameter and its layout.
+ //
+ // An easy transformation of this kind is to take uniform
+ // parameters of a shader entry point and move them into
+ // the global scope instead.
+ //
+ moveEntryPointUniformParamsToGlobalScope(irModule);
+
// Desguar any union types, since these will be illegal on
// various targets.
//
diff --git a/source/slang/ir-entry-point-uniforms.cpp b/source/slang/ir-entry-point-uniforms.cpp
new file mode 100644
index 000000000..64deec1c5
--- /dev/null
+++ b/source/slang/ir-entry-point-uniforms.cpp
@@ -0,0 +1,423 @@
+// ir-entry-point-uniforms.cpp
+#include "ir-entry-point-uniforms.h"
+
+#include "ir.h"
+#include "ir-insts.h"
+
+#include "mangle.h"
+
+namespace Slang
+{
+
+
+// The transformation in this file will solve the problem of taking
+// code like the following:
+//
+// float4 fragmentMain(
+// uniform Texture2D t,
+// uniform SamplerState s;
+// uniform float4 c,
+// float2 uv : UV) : SV_Target
+// {
+// return t.Sample(s, uv) + c;
+// }
+//
+// and transforming into code like this:
+//
+// struct Params
+// {
+// Texture2D t;
+// SamplerState s;
+// float4 c;
+// }
+// ConstantBuffer<Params> params;
+//
+// float4 fragmentMain(
+// float2 uv : UV) : SV_Target
+// {
+// return params.t.Sample(params.s, uv) + params.c;
+// }
+//
+// As can be seen in this example, the `uniform` parameters
+// declared as entry point parameters have been moved into
+// a `struct` declaration that we then use to declare a global
+// shader parameter that is a `ConstantBuffer`. We then
+// rewrite references to those parameters to refer to the
+// contents of the new constant buffer instead.
+//
+// We perform this transformation after the target-specific
+// linking step, because that will have attached layout information
+// to the entry point and its parameters. We need that layout
+// information so that we can:
+//
+// * Identify which parameters are uniform vs. varying.
+// * Have an appropriate layout to attached to the synthesized
+// global shader parameter `params`.
+//
+// One additional wrinkle this pass has to deal with is that
+// in the case where the shader doesn't have any "ordinary"
+// uniform parameters like `c` (e.g., it only has resource/object
+// parameters), we do *not* wrap the parameter `struct` in
+// a `ConstantBuffer`. For example, suppose we have:
+//
+// float4 fragmentMain(
+// uniform Texture2D t,
+// uniform SamplerState s;
+// float2 uv : UV) : SV_Target
+// {
+// return t.Sample(s, uv);
+// }
+//
+// In this case the output of the transformation shold be:
+//
+// struct Params
+// {
+// Texture2D t;
+// SamplerState s;
+// }
+// Params params;
+//
+// float4 fragmentMain(
+// float2 uv : UV) : SV_Target
+// {
+// return params.t.Sample(params.s, uv) + params.c;
+// }
+//
+// Note that this pass should always come before type legalization,
+// which will take responsibility for turning a variable like
+// `params` above into individual variables for the `t` and
+// `s` fields.
+
+// The overall structure here is similar to many other IR passes.
+// We define a "context" structure to encapsulate the pass.
+//
+struct MoveEntryPointUniformParametersToGlobalScope
+{
+ // We'll hang on to the module we are processing,
+ // so that we can refer to it when setting up `IRBuilder`s.
+ //
+ IRModule* module;
+
+ // We will process a whole module by visiting all
+ // its global functions, looking for entry points.
+ //
+ void processModule()
+ {
+ // Note that we are only looking at true global-scope
+ // functions and not functions nested inside of
+ // IR generics. When using generic entry points, this
+ // pass should be run after the entry point(s) have
+ // been specialized to their generic type parameters.
+
+ for( auto inst : module->getGlobalInsts() )
+ {
+ // We are only interested in entry points.
+ //
+ // Every entry point must be a function.
+ //
+ auto func = as<IRFunc>(inst);
+ if( !func )
+ continue;
+
+ // Entry points will always have the `[entryPoint]`
+ // decoration to differentiate them from ordinary
+ // functions.
+ //
+ // TODO: we could make `IREntryPoint` a subclass of
+ // `IRFunc` if desired, to avoid having to attach
+ // an explicit decoration to identify them.
+ //
+ if( !func->findDecorationImpl(kIROp_EntryPointDecoration) )
+ continue;
+
+ // If we fine a candidate entry point, then we
+ // will process it.
+ //
+ processEntryPoint(func);
+ }
+ }
+
+ void processEntryPoint(IRFunc* func)
+ {
+ // We expect all entry points to have explicit layout information attached.
+ //
+ // We will assert that we have the information we need, but try to be
+ // defensive and bail out in the failure case in release builds.
+ //
+ auto funcLayoutDecoration = func->findDecoration<IRLayoutDecoration>();
+ SLANG_ASSERT(funcLayoutDecoration);
+ if(!funcLayoutDecoration)
+ return;
+
+ auto entryPointLayout = dynamic_cast<EntryPointLayout*>(funcLayoutDecoration->getLayout());
+ SLANG_ASSERT(entryPointLayout);
+ if(!entryPointLayout)
+ return;
+
+ // The parameter layout for an entry point will either be a structure
+ // type layout, or a constant buffer (a case of parameter group)
+ // wrapped around such a structure.
+ //
+ // If we are in the latter case we will need to make sure to allocate
+ // an explicit IR constant buffer for that wrapper,
+ //
+ auto entryPointParamsLayout = entryPointLayout->parametersLayout;
+ bool needConstantBuffer = entryPointParamsLayout->typeLayout.as<ParameterGroupTypeLayout>() != nullptr;
+
+ // We will set up an IR builder so that we are ready to generate code.
+ //
+ SharedIRBuilder sharedBuilderStorage;
+ auto sharedBuilder = &sharedBuilderStorage;
+ sharedBuilder->module = module;
+ sharedBuilder->session = module->getSession();
+
+ IRBuilder builderStorage;
+ auto builder = &builderStorage;
+ builder->sharedBuilder = sharedBuilder;
+
+ // *If* the entry point has any uniform parameter then we want to create a
+ // structure type to house them, and a global shader parameter (either
+ // an instance of that type or a constant buffer).
+ //
+ // We only want to create these if actually needed, so we will declare
+ // them here and then initialize them on-demand.
+ //
+ IRStructType* paramStructType = nullptr;
+ IRGlobalParam* globalParam = nullptr;
+
+ // We will be removing any uniform parameters we run into, so we
+ // need to iterate the parameter list carefully to deal with
+ // us modifying it along the way.
+ //
+ IRParam* nextParam = nullptr;
+ for( IRParam* param = func->getFirstParam(); param; param = nextParam )
+ {
+ nextParam = param->getNextParam();
+
+ // We expect all entry-point parameters to have layout information,
+ // but we will be defensive and skip parameters without the required
+ // information when we are in a release build.
+ //
+ auto layoutDecoration = param->findDecoration<IRLayoutDecoration>();
+ SLANG_ASSERT(layoutDecoration);
+ if(!layoutDecoration)
+ continue;
+ auto paramLayout = dynamic_cast<VarLayout*>(layoutDecoration->getLayout());
+ SLANG_ASSERT(paramLayout);
+ if(!paramLayout)
+ continue;
+
+ // A parameter that has varying input/output behavior should be left alone,
+ // since this pass is only supposed to apply to uniform (non-varying)
+ // parameters.
+ //
+ if(isVaryingParameter(paramLayout))
+ continue;
+
+ // At this point we know that `param` is not a varying shader parameter,
+ // so that we want to turn it into an equivalent global shader parameter.
+ //
+ // If this is the first parameter we are running into, then we need
+ // to deal with creating the structure type and global shader
+ // parameter that our transformed entry point will use.
+ //
+ if( !paramStructType )
+ {
+ // First we create the structure to hold the parameters.
+ //
+ builder->setInsertBefore(func);
+ paramStructType = builder->createStructType();
+
+ if( needConstantBuffer )
+ {
+ // If we need a constant buffer, then the global
+ // shader parameter will be a `ConstantBuffer<paramStructType>`
+ //
+ auto constantBufferType = builder->getConstantBufferType(paramStructType);
+ globalParam = builder->createGlobalParam(constantBufferType);
+ }
+ else
+ {
+ // Otherwise, the global shader parameter is just
+ // an instance of `paramStructType`.
+ //
+ globalParam = builder->createGlobalParam(paramStructType);
+ }
+
+ // No matter what, the global shader parameter should have the layout
+ // information from the entry point attached to it, so that the
+ // contained parameters will end up in the right place(s).
+ //
+ builder->addLayoutDecoration(globalParam, entryPointParamsLayout);
+ }
+
+ // Now that we've ensured the global `struct` type and shader paramter
+ // exist, we need to add a field to the `struct` to represent the
+ // current parameter.
+ //
+
+ auto paramType = param->getFullType();
+
+ builder->setInsertBefore(paramStructType);
+ auto paramFieldKey = builder->createStructKey();
+ auto paramField = builder->createStructField(paramStructType, paramFieldKey, paramType);
+ SLANG_UNUSED(paramField);
+
+ // We will transfer all decorations on the parameter over to the key
+ // so that they can affect downstream emit logic.
+ //
+ // TODO: We should double-check whether any of the decorations should
+ // be moved to the *field* instead.
+ //
+ param->transferDecorationsTo(paramFieldKey);
+
+ // There is a bit of a hacky issue, where downstream passes (notably
+ // type legalization) require the field keys for `struct` types to
+ // have mangled names, because those mangled names will be used to
+ // lookup field layout information inside of the layout information
+ // for the `struct` type.
+ //
+ // TODO: We should fix that design choice in how layout information
+ // is stored, to avoid the reliance on name strings.
+ //
+ builder->addExportDecoration(paramFieldKey, getMangledName(paramLayout->varDecl).getUnownedSlice());
+
+ // At this point we want to eliminate the original entry point
+ // parameter, in favor of the `struct` field we declared.
+ // That required replacing any uses of the parameter with
+ // appropriate code to pull out the field.
+ //
+ // We *could* extract the field at the start of the shader
+ // and then do a `replaceAllUsesWith` to propragate it
+ // down, but in practice we expect that it is better for
+ // performance to "rematerialize" the value of a shader
+ // parameter as close to where it is used as possible.
+ //
+ // We are therefore going to replace the uses one at a time.
+ //
+ while(auto use = param->firstUse )
+ {
+ // Given a `use` of the paramter, we will insert
+ // the replacement code right before the instruction
+ // that is doing the using.
+ //
+ builder->setInsertBefore(use->getUser());
+
+ // The way to extract the field that corresponds
+ // to the parameter depends on whether or not
+ // we generated a constant buffer.
+ //
+ IRInst* fieldVal = nullptr;
+ if( needConstantBuffer )
+ {
+ // A constant buffer behaves like a pointer
+ // at the IR level, so we first do a pointer
+ // offset operation to compute what amounts
+ // to `&cb->field`, and then load from that address.
+ //
+ auto fieldAddress = builder->emitFieldAddress(
+ builder->getPtrType(paramType),
+ globalParam,
+ paramFieldKey);
+ fieldVal = builder->emitLoad(fieldAddress);
+ }
+ else
+ {
+ // In the ordinary struct case, the parameter
+ // has an ordinary `struct` type (not a pointer),
+ // so we just extract the field directly.
+ //
+ fieldVal = builder->emitFieldExtract(
+ paramType,
+ globalParam,
+ paramFieldKey);
+ }
+
+ // We replace the value used at this use site, which
+ // will have a side effect of making `use` no longer
+ // be on the list of uses for `param`, so that when
+ // we get back to the top of the loop the list of
+ // uses will be shorter.
+ //
+ use->set(fieldVal);
+ }
+
+ // Once we've replaced all the uses of `param`, we
+ // can go ahead and remove it completely.
+ //
+ param->removeAndDeallocate();
+ }
+ }
+
+ // We need to be able to determine if a parameter is logically
+ // a "varying" parameter based on its layout.
+ //
+ bool isVaryingParameter(VarLayout* layout)
+ {
+ // If *any* of the resources consumed by the parameter
+ // is a varying resource kind (e.g., varying input) then
+ // we consider the whole parameter to be varying.
+ //
+ // This is reasonable because there is no way to declare
+ // a parameter that mixes varying and non-varying fields.
+ //
+ for( auto resInfo : layout->resourceInfos )
+ {
+ if(isVaryingResourceKind(resInfo.kind))
+ return true;
+ }
+
+ // Varying parameters with "system value" semantics currently show up as
+ // consuming no resources, so we need to special-case that here.
+ //
+ // Note: an empty `struct` parameter would also show up the same way, but
+ // we should eliminate any such parameters later on during type legalization.
+ //
+ if(layout->resourceInfos.Count() == 0)
+ return true;
+
+ // if none of the above tests determined that the
+ // parameter was varying, then we can safely consider
+ // it to be non-varying (uniform):
+ return false;
+ }
+
+ // In order to determine whether a parameter is varying based on its
+ // layout, we need to know which resource kinds represent varying
+ // shader parameters.
+ //
+ bool isVaryingResourceKind(LayoutResourceKind kind)
+ {
+ switch( kind )
+ {
+ default:
+ return false;
+
+ // Note: The set of cases that are considered
+ // varying here would need to be extended if we
+ // add more fine-grained resource kinds (e.g.,
+ // if we ever add an explicit resource kind
+ // for geometry shader output streams).
+ //
+ // Ordinary varying input/output:
+ case LayoutResourceKind::VaryingInput:
+ case LayoutResourceKind::VaryingOutput:
+ //
+ // Ray-tracing shader input/output:
+ case LayoutResourceKind::CallablePayload:
+ case LayoutResourceKind::HitAttributes:
+ case LayoutResourceKind::RayPayload:
+ return true;
+ }
+ }
+};
+
+void moveEntryPointUniformParamsToGlobalScope(
+ IRModule* module)
+{
+ MoveEntryPointUniformParametersToGlobalScope context;
+ context.module = module;
+ context.processModule();
+}
+
+}
diff --git a/source/slang/ir-entry-point-uniforms.h b/source/slang/ir-entry-point-uniforms.h
new file mode 100644
index 000000000..5fcfab167
--- /dev/null
+++ b/source/slang/ir-entry-point-uniforms.h
@@ -0,0 +1,12 @@
+// ir-entry-point-uniform.h
+#pragma once
+
+namespace Slang
+{
+struct IRModule;
+
+ /// Move any uniform parameters of entry points to the global scope instead.
+void moveEntryPointUniformParamsToGlobalScope(
+ IRModule* module);
+
+}
diff --git a/source/slang/ir-glsl-legalize.cpp b/source/slang/ir-glsl-legalize.cpp
index 0c49667d8..7dc88a0fa 100644
--- a/source/slang/ir-glsl-legalize.cpp
+++ b/source/slang/ir-glsl-legalize.cpp
@@ -1516,18 +1516,16 @@ void legalizeEntryPointForGLSL(
// to be at the start of the "ordinary" instructions in the block:
builder.setInsertBefore(firstBlock->getFirstOrdinaryInst());
- UInt paramCounter = 0;
for( auto pp = firstBlock->getFirstParam(); pp; pp = pp->getNextParam() )
{
- UInt paramIndex = paramCounter++;
-
- // We assume that the entry-point layout includes information
- // on each parameter, and that these arrays are kept aligned.
- // Note that this means that any transformations that mess
- // with function signatures will need to also update layout info...
+ // We assume that the entry-point parameters will all have
+ // layout information attached to them, which is kept up-to-date
+ // by any transformations affecting the parameter list.
//
- SLANG_ASSERT(entryPointLayout->fields.Count() > paramIndex);
- auto paramLayout = entryPointLayout->fields[paramIndex];
+ auto paramLayoutDecoration = pp->findDecoration<IRLayoutDecoration>();
+ SLANG_ASSERT(paramLayoutDecoration);
+ auto paramLayout = dynamic_cast<VarLayout*>(paramLayoutDecoration->getLayout());
+ SLANG_ASSERT(paramLayout);
legalizeEntryPointParameterForGLSL(
&context,
diff --git a/source/slang/ir-insts.h b/source/slang/ir-insts.h
index 8662569ba..6b12612ef 100644
--- a/source/slang/ir-insts.h
+++ b/source/slang/ir-insts.h
@@ -759,6 +759,9 @@ struct IRBuilder
return getFuncType(paramTypes.Count(), paramTypes.Buffer(), resultType);
}
+ IRConstantBufferType* getConstantBufferType(
+ IRType* elementType);
+
IRConstExprRate* getConstExprRate();
IRGroupSharedRate* getGroupSharedRate();
diff --git a/source/slang/ir-link.cpp b/source/slang/ir-link.cpp
index 610658d51..dba4fc2d1 100644
--- a/source/slang/ir-link.cpp
+++ b/source/slang/ir-link.cpp
@@ -8,9 +8,6 @@
namespace Slang
{
-StructTypeLayout* getGlobalStructLayout(
- ProgramLayout* programLayout);
-
// Needed for lookup up entry-point layouts.
//
// TODO: maybe arrange so that codegen is driven from the layout layer
@@ -721,14 +718,15 @@ IRFunc* specializeIRForEntryPoint(
// than having to look it up on the original entry-point layout.
if( auto firstBlock = clonedFunc->getFirstBlock() )
{
- UInt paramLayoutCount = entryPointLayout->fields.Count();
+ auto paramsStructLayout = getScopeStructLayout(entryPointLayout);
+ UInt paramLayoutCount = paramsStructLayout->fields.Count();
UInt paramCounter = 0;
for( auto pp = firstBlock->getFirstParam(); pp; pp = pp->getNextParam() )
{
UInt paramIndex = paramCounter++;
if( paramIndex < paramLayoutCount )
{
- auto paramLayout = entryPointLayout->fields[paramIndex];
+ auto paramLayout = paramsStructLayout->fields[paramIndex];
context->builder->addLayoutDecoration(
pp,
paramLayout);
@@ -1227,7 +1225,7 @@ IRSpecializationState* createIRSpecializationState(
// Next, we want to optimize lookup for layout infromation
// associated with global declarations, so that we can
// look things up based on the IR values (using mangled names)
- auto globalStructLayout = getGlobalStructLayout(newProgramLayout);
+ auto globalStructLayout = getScopeStructLayout(newProgramLayout);
for (auto globalVarLayout : globalStructLayout->fields)
{
auto mangledName = getMangledName(globalVarLayout->varDecl);
@@ -1235,6 +1233,10 @@ IRSpecializationState* createIRSpecializationState(
}
// for now, clone all unreferenced witness tables
+ //
+ // TODO: This step should *not* be needed with the current IR
+ // specialization approach, so we should consider removing it.
+ //
for (auto sym :context->getSymbols())
{
if (sym.Value->irGlobalValue->op == kIROp_WitnessTable)
diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp
index f622804b2..0a5b8491c 100644
--- a/source/slang/ir.cpp
+++ b/source/slang/ir.cpp
@@ -1710,6 +1710,15 @@ namespace Slang
(IRInst* const*) paramTypes);
}
+ IRConstantBufferType* IRBuilder::getConstantBufferType(IRType* elementType)
+ {
+ IRInst* operands[] = { elementType };
+ return (IRConstantBufferType*) getType(
+ kIROp_ConstantBufferType,
+ 1,
+ operands);
+ }
+
IRConstExprRate* IRBuilder::getConstExprRate()
{
return (IRConstExprRate*)getType(kIROp_ConstExprRate);
diff --git a/source/slang/parameter-binding.cpp b/source/slang/parameter-binding.cpp
index 076b29659..904ec3129 100644
--- a/source/slang/parameter-binding.cpp
+++ b/source/slang/parameter-binding.cpp
@@ -15,7 +15,7 @@ struct ParameterInfo;
struct UsedRange
{
// What parameter has claimed this range?
- ParameterInfo* parameter = nullptr;
+ VarLayout* parameter;
// Begin/end of the range (half-open interval)
UInt begin;
@@ -69,7 +69,7 @@ struct UsedRanges
// then we return that parameter so that the
// caller can issue an error.
//
- ParameterInfo* Add(UsedRange range)
+ VarLayout* Add(UsedRange range)
{
// The invariant on entry to this
// function is that the `ranges` array
@@ -86,8 +86,8 @@ struct UsedRanges
// match the parameter on `range`, so that
// the compiler can issue useful diagnostics.
//
- ParameterInfo* newParam = range.parameter;
- ParameterInfo* existingParam = nullptr;
+ VarLayout* newParam = range.parameter;
+ VarLayout* existingParam = nullptr;
// A clever algorithm might use a binary
// search to identify the first entry in `ranges`
@@ -210,7 +210,7 @@ struct UsedRanges
return existingParam;
}
- ParameterInfo* Add(ParameterInfo* param, UInt begin, UInt end)
+ VarLayout* Add(VarLayout* param, UInt begin, UInt end)
{
UsedRange range;
range.parameter = param;
@@ -219,7 +219,7 @@ struct UsedRanges
return Add(range);
}
- ParameterInfo* Add(ParameterInfo* param, UInt begin, LayoutSize end)
+ VarLayout* Add(VarLayout* param, UInt begin, LayoutSize end)
{
UsedRange range;
range.parameter = param;
@@ -246,7 +246,7 @@ struct UsedRanges
// Try to find space for `count` entries
- UInt Allocate(ParameterInfo* param, UInt count)
+ UInt Allocate(VarLayout* param, UInt count)
{
UInt begin = 0;
@@ -279,11 +279,16 @@ struct UsedRanges
struct ParameterBindingInfo
{
- size_t space;
- size_t index;
+ size_t space = 0;
+ size_t index = 0;
LayoutSize count;
};
+struct ParameterBindingAndKindInfo : ParameterBindingInfo
+{
+ LayoutResourceKind kind = LayoutResourceKind::None;
+};
+
enum
{
kLayoutResourceKindCount = SLANG_PARAMETER_CATEGORY_COUNT,
@@ -353,11 +358,6 @@ struct SharedParameterBindingContext
//
Dictionary<UInt, RefPtr<UsedRangeSet>> globalSpaceUsedRangeSets;
- // What ranges of resource bindings are claimed for particular translation unit?
- // This is only used for varying input/output.
- //
- Dictionary<TranslationUnitRequest*, RefPtr<UsedRangeSet>> translationUnitUsedRangeSets;
-
// Which register spaces have been claimed so far?
UsedRanges usedSpaces;
@@ -1098,91 +1098,16 @@ RefPtr<Type> tryGetEffectiveTypeForGLSLVaryingOutput(
return nullptr;
}
-RefPtr<TypeLayout>
-getTypeLayoutForGlobalShaderParameter_GLSL(
+ /// Determine how to lay out a global variable that might be a shader parameter.
+ ///
+ /// Returns `nullptr` if the declaration does not represent a shader parameter.
+RefPtr<TypeLayout> getTypeLayoutForGlobalShaderParameter(
ParameterBindingContext* context,
- VarDeclBase* varDecl)
-{
- auto layoutContext = context->layoutContext;
- auto rules = layoutContext.getRulesFamily();
- auto type = varDecl->getType();
-
- // A GLSL shader parameter will be marked with
- // a qualifier to match the boundary it uses
- //
- // In the case of a parameter block, we will have
- // consumed this qualifier as part of parsing,
- // so that it won't be present on the declaration
- // any more. As such we also inspect the type
- // of the variable.
-
- // We want to check for a constant-buffer type with a `push_constant` layout
- // qualifier before we move on to anything else.
- if( varDecl->HasModifier<PushConstantAttribute>() && as<ConstantBufferType>(type) )
- {
- return CreateTypeLayout(
- layoutContext.with(rules->getPushConstantBufferRules()),
- type);
- }
-
- // TODO(tfoley): We have multiple variations of
- // the `uniform` modifier right now, and that
- // needs to get fixed...
- if( varDecl->HasModifier<HLSLUniformModifier>() || as<ConstantBufferType>(type) )
- {
- return CreateTypeLayout(
- layoutContext.with(rules->getConstantBufferRules()),
- type);
- }
-
- if( varDecl->HasModifier<GLSLBufferModifier>() || as<GLSLShaderStorageBufferType>(type) )
- {
- return CreateTypeLayout(
- layoutContext.with(rules->getShaderStorageBufferRules()),
- type);
- }
-
- if (auto effectiveVaryingInputType = tryGetEffectiveTypeForGLSLVaryingInput(context, varDecl))
- {
- // We expect to handle these elsewhere
- SLANG_DIAGNOSE_UNEXPECTED(getSink(context), varDecl, "GLSL varying input");
- return CreateTypeLayout(
- layoutContext.with(rules->getVaryingInputRules()),
- effectiveVaryingInputType);
- }
-
- if (auto effectiveVaryingOutputType = tryGetEffectiveTypeForGLSLVaryingOutput(context, varDecl))
- {
- // We expect to handle these elsewhere
- SLANG_DIAGNOSE_UNEXPECTED(getSink(context), varDecl, "GLSL varying output");
- return CreateTypeLayout(
- layoutContext.with(rules->getVaryingOutputRules()),
- effectiveVaryingOutputType);
- }
-
- // A `const` global with a `layout(constant_id = ...)` modifier
- // is a declaration of a specialization constant.
- if( varDecl->HasModifier<GLSLConstantIDLayoutModifier>() )
- {
- return CreateTypeLayout(
- layoutContext.with(rules->getSpecializationConstantRules()),
- type);
- }
-
- // GLSL says that an "ordinary" global variable
- // is just a (thread local) global and not a
- // parameter
- return nullptr;
-}
-
-RefPtr<TypeLayout>
-getTypeLayoutForGlobalShaderParameter_HLSL(
- ParameterBindingContext* context,
- VarDeclBase* varDecl)
+ VarDeclBase* varDecl,
+ Type* type)
{
auto layoutContext = context->layoutContext;
auto rules = layoutContext.getRulesFamily();
- auto type = varDecl->getType();
if( varDecl->HasModifier<ShaderRecordNVLayoutModifier>() && as<ConstantBufferType>(type) )
{
@@ -1217,32 +1142,13 @@ getTypeLayoutForGlobalShaderParameter_HLSL(
type);
}
-// Determine how to lay out a global variable that might be
-// a shader parameter.
-// Returns `nullptr` if the declaration does not represent
-// a shader parameter.
-
-RefPtr<TypeLayout>
-getTypeLayoutForGlobalShaderParameter(
+RefPtr<TypeLayout> getTypeLayoutForGlobalShaderParameter(
ParameterBindingContext* context,
VarDeclBase* varDecl)
{
- switch( context->sourceLanguage )
- {
- case SourceLanguage::Slang:
- case SourceLanguage::HLSL:
- return getTypeLayoutForGlobalShaderParameter_HLSL(context, varDecl);
-
- case SourceLanguage::GLSL:
- return getTypeLayoutForGlobalShaderParameter_GLSL(context, varDecl);
-
- default:
- SLANG_UNEXPECTED("unhandled source language");
- UNREACHABLE_RETURN(nullptr);
- }
+ return getTypeLayoutForGlobalShaderParameter(context, varDecl, varDecl->getType());
}
-
//
enum EntryPointParameterDirection
@@ -1264,44 +1170,12 @@ struct EntryPointParameterState
};
-static RefPtr<TypeLayout> processEntryPointParameter(
+static RefPtr<TypeLayout> processEntryPointVaryingParameter(
ParameterBindingContext* context,
RefPtr<Type> type,
EntryPointParameterState const& state,
RefPtr<VarLayout> varLayout);
-static void collectGlobalScopeGLSLVaryingParameter(
- ParameterBindingContext* context,
- RefPtr<VarDeclBase> varDecl,
- RefPtr<Type> effectiveType,
- EntryPointParameterDirection direction)
-{
- int defaultSemanticIndex = 0;
-
- EntryPointParameterState state;
- state.directionMask = direction;
- state.ioSemanticIndex = &defaultSemanticIndex;
- state.stage = context->stage;
- state.loc = varDecl->loc;
-
- RefPtr<VarLayout> varLayout = new VarLayout();
- varLayout->varDecl = makeDeclRef(varDecl.Ptr());
-
- varLayout->typeLayout = processEntryPointParameter(
- context,
- effectiveType,
- state,
- varLayout);
-
- // Now add it to our list of reflection parameters, so
- // that it can get a location assigned later...
-
- ParameterInfo* parameterInfo = new ParameterInfo();
- parameterInfo->translationUnit = context->translationUnit;
- context->shared->parameters.Add(parameterInfo);
- parameterInfo->varLayouts.Add(varLayout);
-}
-
// Collect a single declaration into our set of parameters
static void collectGlobalGenericParameter(
ParameterBindingContext* context,
@@ -1319,25 +1193,6 @@ static void collectGlobalScopeParameter(
ParameterBindingContext* context,
RefPtr<VarDeclBase> varDecl)
{
- // HACK: We need to intercept GLSL varying `in` and `out` here, way earlier
- // in the process, so that we can avoid all kinds of nastiness that would
- // otherwise be applied to them.
- if (context->sourceLanguage == SourceLanguage::GLSL)
- {
- if (auto effectiveVaryingInputType = tryGetEffectiveTypeForGLSLVaryingInput(context, varDecl))
- {
- collectGlobalScopeGLSLVaryingParameter(context, varDecl, effectiveVaryingInputType, kEntryPointParameterDirection_Input);
- return;
- }
-
- if (auto effectiveVaryingOutputType = tryGetEffectiveTypeForGLSLVaryingOutput(context, varDecl))
- {
- collectGlobalScopeGLSLVaryingParameter(context, varDecl, effectiveVaryingOutputType, kEntryPointParameterDirection_Output);
- return;
- }
- }
-
-
// We use a single operation to both check whether the
// variable represents a shader parameter, and to compute
// the layout for that parameter's type.
@@ -1422,22 +1277,6 @@ static UInt allocateUnusedSpaces(
return context->shared->usedSpaces.Allocate(nullptr, count);
}
-static RefPtr<UsedRangeSet> findUsedRangeSetForTranslationUnit(
- ParameterBindingContext* context,
- TranslationUnitRequest* translationUnit)
-{
- if (!translationUnit)
- return findUsedRangeSetForSpace(context, 0);
-
- RefPtr<UsedRangeSet> usedRangeSet;
- if (context->shared->translationUnitUsedRangeSets.TryGetValue(translationUnit, usedRangeSet))
- return usedRangeSet;
-
- usedRangeSet = new UsedRangeSet();
- context->shared->translationUnitUsedRangeSets.Add(translationUnit, usedRangeSet);
- return usedRangeSet;
-}
-
static void addExplicitParameterBinding(
ParameterBindingContext* context,
RefPtr<ParameterInfo> parameterInfo,
@@ -1486,15 +1325,15 @@ static void addExplicitParameterBinding(
// need to grab a full space
markSpaceUsed(context, semanticInfo.space);
}
- auto overlappedParameterInfo = usedRangeSet->usedResourceRanges[(int)semanticInfo.kind].Add(
- parameterInfo,
+ auto overlappedVarLayout = usedRangeSet->usedResourceRanges[(int)semanticInfo.kind].Add(
+ parameterInfo->varLayouts[0],
semanticInfo.index,
semanticInfo.index + count);
- if (overlappedParameterInfo)
+ if (overlappedVarLayout)
{
auto paramA = parameterInfo->varLayouts[0]->varDecl.getDecl();
- auto paramB = overlappedParameterInfo->varLayouts[0]->varDecl.getDecl();
+ auto paramB = overlappedVarLayout->varDecl.getDecl();
getSink(context)->diagnose(paramA, Diagnostics::parameterBindingsOverlap,
getReflectionName(paramA),
@@ -1641,22 +1480,6 @@ static void addExplicitParameterBindings_GLSL(
semanticInfo.index = attr->set;
semanticInfo.space = 0;
}
- else if( (resInfo = typeLayout->FindResourceInfo(LayoutResourceKind::VertexInput)) != nullptr )
- {
- // Try to find `location` binding
- if(!findLayoutArg<GLSLLocationLayoutModifier>(varDecl, &semanticInfo.index))
- return;
-
- usedRangeSet = findUsedRangeSetForTranslationUnit(context, parameterInfo->translationUnit);
- }
- else if( (resInfo = typeLayout->FindResourceInfo(LayoutResourceKind::FragmentOutput)) != nullptr )
- {
- // Try to find `location` binding
- if(!findLayoutArg<GLSLLocationLayoutModifier>(varDecl, &semanticInfo.index))
- return;
-
- usedRangeSet = findUsedRangeSetForTranslationUnit(context, parameterInfo->translationUnit);
- }
else if( (resInfo = typeLayout->FindResourceInfo(LayoutResourceKind::SpecializationConstant)) != nullptr )
{
// Try to find `constant_id` binding
@@ -1697,21 +1520,16 @@ void generateParameterBindings(
}
// Generate the binding information for a shader parameter.
-static void completeBindingsForParameter(
+static void completeBindingsForParameterImpl(
ParameterBindingContext* context,
+ RefPtr<VarLayout> firstVarLayout,
+ ParameterBindingInfo bindingInfos[kLayoutResourceKindCount],
RefPtr<ParameterInfo> parameterInfo)
{
// For any resource kind used by the parameter
// we need to update its layout information
// to include a binding for that resource kind.
//
- // We will use the first declaration of the parameter as
- // a stand-in for all the declarations, so it is important
- // that earlier code has validated that the declarations
- // "match".
-
- SLANG_RELEASE_ASSERT(parameterInfo->varLayouts.Count() != 0);
- auto firstVarLayout = parameterInfo->varLayouts.First();
auto firstTypeLayout = firstVarLayout->typeLayout;
// We need to deal with allocation of full register spaces first,
@@ -1731,7 +1549,7 @@ static void completeBindingsForParameter(
// has specified an explicit binding, since those won't
// go into our contiguously allocated range.
//
- auto& bindingInfo = parameterInfo->bindingInfo[(int)kind];
+ auto& bindingInfo = bindingInfos[(int)kind];
if( bindingInfo.count != 0 )
{
continue;
@@ -1798,7 +1616,7 @@ static void completeBindingsForParameter(
// Did we already apply some explicit binding information
// for this resource kind?
auto kind = typeRes.kind;
- auto& bindingInfo = parameterInfo->bindingInfo[(int)kind];
+ auto& bindingInfo = bindingInfos[(int)kind];
if( bindingInfo.count != 0 )
{
// If things have already been bound, our work is done.
@@ -1892,26 +1710,53 @@ static void completeBindingsForParameter(
// space.
UInt space = context->shared->defaultSpace;
-
- RefPtr<UsedRangeSet> usedRangeSet;
- switch (kind)
- {
- default:
- usedRangeSet = findUsedRangeSetForSpace(context, space);
- break;
-
- case LayoutResourceKind::VertexInput:
- case LayoutResourceKind::FragmentOutput:
- usedRangeSet = findUsedRangeSetForTranslationUnit(context, parameterInfo->translationUnit);
- break;
- }
+ RefPtr<UsedRangeSet> usedRangeSet = findUsedRangeSetForSpace(context, space);
bindingInfo.count = count;
- bindingInfo.index = usedRangeSet->usedResourceRanges[(int)kind].Allocate(parameterInfo, count.getFiniteValue());
-
+ bindingInfo.index = usedRangeSet->usedResourceRanges[(int)kind].Allocate(firstVarLayout, count.getFiniteValue());
bindingInfo.space = space;
}
}
+}
+
+static void applyBindingInfoToParameter(
+ RefPtr<VarLayout> varLayout,
+ ParameterBindingInfo bindingInfos[kLayoutResourceKindCount])
+{
+ for(auto k = 0; k < kLayoutResourceKindCount; ++k)
+ {
+ auto kind = LayoutResourceKind(k);
+ auto& bindingInfo = bindingInfos[k];
+
+ // skip resources we aren't consuming
+ if(bindingInfo.count == 0)
+ continue;
+
+ // Add a record to the variable layout
+ auto varRes = varLayout->AddResourceInfo(kind);
+ varRes->space = (int) bindingInfo.space;
+ varRes->index = (int) bindingInfo.index;
+ }
+}
+
+// Generate the binding information for a shader parameter.
+static void completeBindingsForParameter(
+ ParameterBindingContext* context,
+ RefPtr<ParameterInfo> parameterInfo)
+{
+ // We will use the first declaration of the parameter as
+ // a stand-in for all the declarations, so it is important
+ // that earlier code has validated that the declarations
+ // "match".
+
+ SLANG_RELEASE_ASSERT(parameterInfo->varLayouts.Count() != 0);
+ auto firstVarLayout = parameterInfo->varLayouts.First();
+
+ completeBindingsForParameterImpl(
+ context,
+ firstVarLayout,
+ parameterInfo->bindingInfo,
+ parameterInfo);
// At this point we should have explicit binding locations chosen for
// all the relevant resource kinds, so we can apply these to the
@@ -1919,23 +1764,25 @@ static void completeBindingsForParameter(
for(auto& varLayout : parameterInfo->varLayouts)
{
- for(auto k = 0; k < kLayoutResourceKindCount; ++k)
- {
- auto kind = LayoutResourceKind(k);
- auto& bindingInfo = parameterInfo->bindingInfo[k];
-
- // skip resources we aren't consuming
- if(bindingInfo.count == 0)
- continue;
-
- // Add a record to the variable layout
- auto varRes = varLayout->AddResourceInfo(kind);
- varRes->space = (int) bindingInfo.space;
- varRes->index = (int) bindingInfo.index;
- }
+ applyBindingInfoToParameter(varLayout, parameterInfo->bindingInfo);
}
}
+static void completeBindingsForParameter(
+ ParameterBindingContext* context,
+ RefPtr<VarLayout> varLayout)
+{
+ ParameterBindingInfo bindingInfos[kLayoutResourceKindCount];
+ completeBindingsForParameterImpl(
+ context,
+ varLayout,
+ bindingInfos,
+ nullptr);
+ applyBindingInfoToParameter(varLayout, bindingInfos);
+}
+
+
+
static void collectGlobalScopeParameters(
ParameterBindingContext* context,
ModuleDecl* program)
@@ -2121,7 +1968,7 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter(
return typeLayout;
}
-static RefPtr<TypeLayout> processEntryPointParameterDecl(
+static RefPtr<TypeLayout> processEntryPointVaryingParameterDecl(
ParameterBindingContext* context,
Decl* decl,
RefPtr<Type> type,
@@ -2159,20 +2006,15 @@ static RefPtr<TypeLayout> processEntryPointParameterDecl(
// *or* we couldn't find an explicit semantic to apply on the given
// declaration, so we will just recursive with whatever we have at
// the moment.
- return processEntryPointParameter(context, type, state, varLayout);
+ return processEntryPointVaryingParameter(context, type, state, varLayout);
}
-static RefPtr<TypeLayout> processEntryPointParameter(
+static RefPtr<TypeLayout> processEntryPointVaryingParameter(
ParameterBindingContext* context,
RefPtr<Type> type,
EntryPointParameterState const& state,
RefPtr<VarLayout> varLayout)
{
- if (varLayout)
- {
- varLayout->stage = state.stage;
- }
-
// The default handling of varying parameters should not apply
// to geometry shader output streams; they have their own special rules.
if( auto gsStreamType = as<HLSLStreamOutputType>(type) )
@@ -2192,7 +2034,7 @@ static RefPtr<TypeLayout> processEntryPointParameter(
elementState.stage = state.stage;
elementState.loc = state.loc;
- auto elementTypeLayout = processEntryPointParameter(context, elementType, elementState, nullptr);
+ auto elementTypeLayout = processEntryPointVaryingParameter(context, elementType, elementState, nullptr);
RefPtr<StreamOutputTypeLayout> typeLayout = new StreamOutputTypeLayout();
typeLayout->type = type;
@@ -2316,13 +2158,13 @@ static RefPtr<TypeLayout> processEntryPointParameter(
auto elementCount = (UInt) GetIntVal(arrayType->ArrayLength);
// We use the first element to derive the layout for the element type
- auto elementTypeLayout = processEntryPointParameter(context, arrayType->baseType, state, varLayout);
+ auto elementTypeLayout = processEntryPointVaryingParameter(context, arrayType->baseType, state, varLayout);
// We still walk over subsequent elements to make sure they consume resources
// as needed
for( UInt ii = 1; ii < elementCount; ++ii )
{
- processEntryPointParameter(context, arrayType->baseType, state, nullptr);
+ processEntryPointVaryingParameter(context, arrayType->baseType, state, nullptr);
}
RefPtr<ArrayTypeLayout> arrayTypeLayout = new ArrayTypeLayout();
@@ -2357,7 +2199,7 @@ static RefPtr<TypeLayout> processEntryPointParameter(
RefPtr<VarLayout> fieldVarLayout = new VarLayout();
fieldVarLayout->varDecl = field;
- auto fieldTypeLayout = processEntryPointParameterDecl(
+ auto fieldTypeLayout = processEntryPointVaryingParameterDecl(
context,
field.getDecl(),
GetType(field),
@@ -2409,6 +2251,210 @@ static RefPtr<TypeLayout> processEntryPointParameter(
UNREACHABLE_RETURN(nullptr);
}
+ /// Compute the type layout for a parameter declared directly on an entry point.
+static RefPtr<TypeLayout> computeEntryPointParameterTypeLayout(
+ ParameterBindingContext* context,
+ SubstitutionSet typeSubst,
+ RefPtr<ParamDecl> paramDecl,
+ RefPtr<VarLayout> paramVarLayout,
+ EntryPointParameterState& state)
+{
+ auto paramType = paramDecl->type.type->Substitute(typeSubst).as<Type>();
+
+ if( paramDecl->HasModifier<HLSLUniformModifier>() )
+ {
+ // An entry-point parameter that is explicitly marked `uniform` represents
+ // a uniform shader parameter passed via the implicitly-defined
+ // constant buffer (e.g., the `$Params` constant buffer seen in fxc/dxc output).
+ //
+ return CreateTypeLayout(
+ context->layoutContext.with(context->getRulesFamily()->getConstantBufferRules()),
+ paramType);
+ }
+ else
+ {
+ // The default case is a varying shader parameter, which could be used for
+ // input, output, or both.
+ //
+ // The varying case needs to not only compute a layout, but also assocaite
+ // "semantic" strings/indices with the varying parameters by recursively
+ // walking their structure.
+
+ state.directionMask = 0;
+
+ // If it appears to be an input, process it as such.
+ if( paramDecl->HasModifier<InModifier>() || paramDecl->HasModifier<InOutModifier>() || !paramDecl->HasModifier<OutModifier>() )
+ {
+ state.directionMask |= kEntryPointParameterDirection_Input;
+ }
+
+ // If it appears to be an output, process it as such.
+ if(paramDecl->HasModifier<OutModifier>() || paramDecl->HasModifier<InOutModifier>())
+ {
+ state.directionMask |= kEntryPointParameterDirection_Output;
+ }
+
+ return processEntryPointVaryingParameterDecl(
+ context,
+ paramDecl.Ptr(),
+ paramDecl->type.type->Substitute(typeSubst).as<Type>(),
+ state,
+ paramVarLayout);
+ }
+}
+
+// There are multiple places where we need to compute the layout
+// for a "scope" such as the global scope or an entry point.
+// The `ScopeLayoutBuilder` encapsulates the logic around:
+//
+// * Doing layout for the ordinary/uniform fields, which involves
+// using the `struct` layout rules for constant buffers on
+// the target.
+//
+// * Creating a final type/var layout that reflects whether the
+// scope needs a constant buffer to be allocated to it.
+//
+struct ScopeLayoutBuilder
+{
+ ParameterBindingContext* m_context = nullptr;
+ LayoutRulesImpl* m_rules = nullptr;
+ RefPtr<StructTypeLayout> m_structLayout;
+ UniformLayoutInfo m_structLayoutInfo;
+ bool m_needConstantBuffer = false;
+
+ void beginLayout(
+ ParameterBindingContext* context)
+ {
+ m_context = context;
+ m_rules = context->getRulesFamily()->getConstantBufferRules();
+ m_structLayout = new StructTypeLayout();
+ m_structLayout->rules = m_rules;
+
+ m_structLayoutInfo = m_rules->BeginStructLayout();
+ }
+
+ void _addParameter(
+ RefPtr<VarLayout> firstVarLayout,
+ ParameterInfo* parameterInfo)
+ {
+ // Does the parameter have any uniform data?
+ auto layoutInfo = firstVarLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform);
+ LayoutSize uniformSize = layoutInfo ? layoutInfo->count : 0;
+ if( uniformSize != 0 )
+ {
+ m_needConstantBuffer = true;
+
+ // Make sure uniform fields get laid out properly...
+
+ UniformLayoutInfo fieldInfo(
+ uniformSize,
+ firstVarLayout->typeLayout->uniformAlignment);
+
+ LayoutSize uniformOffset = m_rules->AddStructField(
+ &m_structLayoutInfo,
+ fieldInfo);
+
+ if( parameterInfo )
+ {
+ for( auto& varLayout : parameterInfo->varLayouts )
+ {
+ varLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset.getFiniteValue();
+ }
+ }
+ else
+ {
+ firstVarLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset.getFiniteValue();
+ }
+ }
+
+ m_structLayout->fields.Add(firstVarLayout);
+
+ if( parameterInfo )
+ {
+ for( auto& varLayout : parameterInfo->varLayouts )
+ {
+ m_structLayout->mapVarToLayout.Add(varLayout->varDecl.getDecl(), varLayout);
+ }
+ }
+ else
+ {
+ m_structLayout->mapVarToLayout.Add(firstVarLayout->varDecl.getDecl(), firstVarLayout);
+ }
+ }
+
+ void addParameter(
+ RefPtr<VarLayout> varLayout)
+ {
+ _addParameter(varLayout, nullptr);
+ }
+
+ void addParameter(
+ ParameterInfo* parameterInfo)
+ {
+ SLANG_RELEASE_ASSERT(parameterInfo->varLayouts.Count() != 0);
+ auto firstVarLayout = parameterInfo->varLayouts.First();
+
+ _addParameter(firstVarLayout, parameterInfo);
+ }
+
+ RefPtr<VarLayout> endLayout()
+ {
+ m_rules->EndStructLayout(&m_structLayoutInfo);
+
+ RefPtr<TypeLayout> scopeTypeLayout = m_structLayout;
+
+ // If the caller decided to allocate a constant buffer for
+ // the ordinary data, then we need to wrap up the structure
+ // type (layout) in a constant buffer type (layout).
+ //
+ if( m_needConstantBuffer )
+ {
+ auto constantBufferLayout = createParameterGroupTypeLayout(
+ m_context->layoutContext,
+ nullptr,
+ m_rules,
+ m_rules->GetObjectLayout(ShaderParameterKind::ConstantBuffer),
+ m_structLayout);
+
+ scopeTypeLayout = constantBufferLayout;
+ }
+
+ // We now have a bunch of layout information, which we should
+ // record into a suitable object that represents the scope
+ RefPtr<VarLayout> scopeVarLayout = new VarLayout();
+ scopeVarLayout->typeLayout = scopeTypeLayout;
+ return scopeVarLayout;
+ }
+};
+
+ /// Helper routine to allocate a constant buffer binding if one is needed.
+ ///
+ /// This function primarily exists to encapsulate the logic for allocating
+ /// the resources required for a constant buffer in the appropriate
+ /// target-specific fashion.
+ ///
+static ParameterBindingAndKindInfo maybeAllocateConstantBufferBinding(
+ ParameterBindingContext* context,
+ bool needConstantBuffer)
+{
+ if( !needConstantBuffer ) return ParameterBindingAndKindInfo();
+
+ UInt space = context->shared->defaultSpace;
+ auto usedRangeSet = findUsedRangeSetForSpace(context, space);
+
+ auto layoutInfo = context->getRulesFamily()->getConstantBufferRules()->GetObjectLayout(
+ ShaderParameterKind::ConstantBuffer);
+
+ ParameterBindingAndKindInfo info;
+ info.kind = layoutInfo.kind;
+ info.count = layoutInfo.size;
+ info.index = usedRangeSet->usedResourceRanges[(int)layoutInfo.kind].Allocate(nullptr, layoutInfo.size.getFiniteValue());
+ info.space = space;
+ return info;
+}
+
+ /// Iterate over the parameters of an entry point to compute its requirements.
+ ///
static void collectEntryPointParameters(
ParameterBindingContext* context,
EntryPointRequest* entryPoint,
@@ -2420,17 +2466,27 @@ static void collectEntryPointParameters(
// Something must have failed earlier, so that
// we didn't find a declaration to match this
// entry point request.
+ //
return;
}
- // Create the layout object here
- auto entryPointLayout = new EntryPointLayout();
+ // We will take responsibility for creating and filling in
+ // the `EntryPointLayout` object here.
+ //
+ RefPtr<EntryPointLayout> entryPointLayout = new EntryPointLayout();
entryPointLayout->profile = entryPoint->profile;
entryPointLayout->entryPoint = entryPointFuncDecl;
- context->entryPointLayout = entryPointLayout;
+ // The entry point layout must be added to the output
+ // program layout so that it can be accessed by reflection.
+ //
context->shared->programLayout->entryPoints.Add(entryPointLayout);
+ // For the duration of our parameter collection work we will
+ // establish this entry point as the current one in the context.
+ //
+ context->entryPointLayout = entryPointLayout;
+
// Note: this isn't really the best place for this logic to sit,
// but it is the simplest place where we have a direct correspondence
// between a single `EntryPointRequest` and its matching `EntryPointLayout`,
@@ -2443,77 +2499,105 @@ static void collectEntryPointParameters(
entryPointLayout->taggedUnionTypeLayouts.Add(typeLayout);
}
- // Okay, we seemingly have an entry-point function, and now we need to collect info on its parameters too
+ // We are going to iterate over the entry-point parameters,
+ // and while we do so we will go ahead and perform layout/binding
+ // assignment for two cases:
//
- // TODO: Long-term we probably want complete information on all inputs/outputs of an entry point,
- // but for now we are really just trying to scrape information on fragment outputs, so lets do that:
+ // First, the varying parameters of the entry point will have
+ // their semantics and locations assigned, so we set up state
+ // for tracking that layout.
//
- // TODO: check whether we should enumerate the parameters before the return type, or vice versa
-
int defaultSemanticIndex = 0;
-
EntryPointParameterState state;
state.ioSemanticIndex = &defaultSemanticIndex;
state.optSemanticName = nullptr;
state.semanticSlotCount = 0;
state.stage = entryPoint->getStage();
- for( auto m : entryPointFuncDecl->Members )
- {
- auto paramDecl = as<VarDeclBase>(m);
- if(!paramDecl)
- continue;
+ // Second, we will compute offsets for any "ordinary" data
+ // in the parameter list (e.g., a `uniform float4x4 mvp` parameter),
+ // which is what the `ScopeLayoutBuilder` is designed to help with.
+ //
+ ScopeLayoutBuilder scopeBuilder;
+ scopeBuilder.beginLayout(context);
+ auto paramsStructLayout = scopeBuilder.m_structLayout;
- // We have an entry-point parameter, and need to figure out what to do with it.
+ for( auto paramDecl : entryPointFuncDecl->getMembersOfType<ParamDecl>() )
+ {
+ // Any error messages we emit during the process should
+ // refer to the location of this parameter.
+ //
state.loc = paramDecl->loc;
- // TODO: need to handle `uniform`-qualified parameters here
- if (paramDecl->HasModifier<HLSLUniformModifier>())
- continue;
-
- state.directionMask = 0;
-
- // If it appears to be an input, process it as such.
- if( paramDecl->HasModifier<InModifier>() || paramDecl->HasModifier<InOutModifier>() || !paramDecl->HasModifier<OutModifier>() )
- {
- state.directionMask |= kEntryPointParameterDirection_Input;
- }
-
- // If it appears to be an output, process it as such.
- if(paramDecl->HasModifier<OutModifier>() || paramDecl->HasModifier<InOutModifier>())
- {
- state.directionMask |= kEntryPointParameterDirection_Output;
- }
-
+ // We are going to construct the variable layout for this
+ // parameter *before* computing the type layout, because
+ // the type layout computation is also determining the effective
+ // semantic of the parameter, which needs to be stored
+ // back onto the `VarLayout`.
+ //
RefPtr<VarLayout> paramVarLayout = new VarLayout();
- paramVarLayout->varDecl = makeDeclRef(paramDecl);
+ paramVarLayout->varDecl = makeDeclRef(paramDecl.Ptr());
+ paramVarLayout->stage = state.stage;
- auto paramTypeLayout = processEntryPointParameterDecl(
+ auto paramTypeLayout = computeEntryPointParameterTypeLayout(
context,
+ typeSubst,
paramDecl,
- paramDecl->type.type->Substitute(typeSubst).dynamicCast<Type>(),
- state,
- paramVarLayout);
+ paramVarLayout,
+ state);
+ paramVarLayout->typeLayout = paramTypeLayout;
- // Skip parameters for which we could not compute a layout
+ // We expect to always be able to compute a layout for
+ // entry-point parameters, but to be defensive we will
+ // skip parameters that couldn't have a layout computed
+ // when assertions are disabled.
+ //
+ SLANG_ASSERT(paramTypeLayout);
if(!paramTypeLayout)
continue;
- paramVarLayout->typeLayout = paramTypeLayout;
+ // Now that we've computed the layout to use for the parameter,
+ // we need to add its resource usage to that of the entry
+ // point as a whole.
+ //
+ // Any "ordinary" data (e.g., a `float4x4`) needs to be accounted
+ // for using the `ScopeLayoutBuilder`, since it will handle
+ // the details of target-specific `struct` type layout.
+ //
+ scopeBuilder.addParameter(paramVarLayout);
- for (auto rr : paramTypeLayout->resourceInfos)
+ // All of the other resources types will be handled in a
+ // simpler loop that just increments the relevant counters.
+ //
+ for (auto paramTypeResInfo : paramTypeLayout->resourceInfos)
{
- auto entryPointRes = entryPointLayout->findOrAddResourceInfo(rr.kind);
- paramVarLayout->findOrAddResourceInfo(rr.kind)->index = entryPointRes->count.getFiniteValue();
- entryPointRes->count += rr.count;
- }
+ // We need to skip ordinary data because it is being
+ // handled by the `scopeBuilder`.
+ //
+ if(paramTypeResInfo.kind == LayoutResourceKind::Uniform)
+ continue;
+
+ // Whatever resources the parameter uses, we need to
+ // assign the parameter's location/register/binding offset to
+ // be the sum of everything added so far.
+ //
+ auto entryPointResInfo = paramsStructLayout->findOrAddResourceInfo(paramTypeResInfo.kind);
+ paramVarLayout->findOrAddResourceInfo(paramTypeResInfo.kind)->index = entryPointResInfo->count.getFiniteValue();
- entryPointLayout->fields.Add(paramVarLayout);
- entryPointLayout->mapVarToLayout.Add(paramDecl, paramVarLayout);
+ // We then need to add the resources consumed by the parameter
+ // to those consumed by the entry point.
+ //
+ entryPointResInfo->count += paramTypeResInfo.count;
+ }
}
+ entryPointLayout->parametersLayout = scopeBuilder.endLayout();
- // If we have a non-`void` output type for the entry point, then process it as
- // an output parameter.
+ // For an entry point with a non-`void` return type, we need to process the
+ // return type as a varying output parameter.
+ //
+ // TODO: Ideally we should make the layout process more robust to empty/void
+ // types and apply this logic unconditionally.
+ //
auto resultType = entryPointFuncDecl->ReturnType.type;
if( !resultType->Equals(resultType->getSession()->getVoidType()) )
{
@@ -2521,8 +2605,9 @@ static void collectEntryPointParameters(
state.directionMask = kEntryPointParameterDirection_Output;
RefPtr<VarLayout> resultLayout = new VarLayout();
+ resultLayout->stage = state.stage;
- auto resultTypeLayout = processEntryPointParameterDecl(
+ auto resultTypeLayout = processEntryPointVaryingParameterDecl(
context,
entryPointFuncDecl,
resultType->Substitute(typeSubst).dynamicCast<Type>(),
@@ -2535,7 +2620,7 @@ static void collectEntryPointParameters(
for (auto rr : resultTypeLayout->resourceInfos)
{
- auto entryPointRes = entryPointLayout->findOrAddResourceInfo(rr.kind);
+ auto entryPointRes = paramsStructLayout->findOrAddResourceInfo(rr.kind);
resultLayout->findOrAddResourceInfo(rr.kind)->index = entryPointRes->count.getFiniteValue();
entryPointRes->count += rr.count;
}
@@ -2687,6 +2772,7 @@ void generateParameterBindings(
context.shared = &sharedContext;
context.translationUnit = nullptr;
context.layoutContext = layoutContext;
+
// Walk through AST to discover all the parameters
collectParameters(&context, compileReq);
@@ -2787,24 +2873,10 @@ void generateParameterBindings(
// If there are any global-scope uniforms, then we need to
// allocate a constant-buffer binding for them here.
- ParameterBindingInfo globalConstantBufferBinding;
- globalConstantBufferBinding.index = 0;
- globalConstantBufferBinding.space = 0;
- if( needDefaultConstantBuffer )
- {
- // TODO: this logic is only correct for D3D targets, where
- // global-scope uniforms get wrapped into a constant buffer.
-
- UInt space = sharedContext.defaultSpace;
- auto usedRangeSet = findUsedRangeSetForSpace(&context, space);
-
- globalConstantBufferBinding.index =
- usedRangeSet->usedResourceRanges[
- (int)LayoutResourceKind::ConstantBuffer].Allocate(nullptr, 1);
-
- globalConstantBufferBinding.space = space;
- }
-
+ //
+ ParameterBindingAndKindInfo globalConstantBufferBinding = maybeAllocateConstantBufferBinding(
+ &context,
+ needDefaultConstantBuffer);
// Now walk through again to actually give everything
// ranges of registers...
@@ -2813,135 +2885,200 @@ void generateParameterBindings(
completeBindingsForParameter(&context, parameter);
}
- // TODO: need to deal with parameters declared inside entry-point
- // parameter lists at some point...
-
-
- // Next we need to create a type layout to reflect the information
- // we have collected.
-
- // We will lay out any bare uniforms at the global scope into
- // a single constant buffer. This is appropriate for HLSL global-scope
- // uniforms, and Vulkan GLSL doesn't allow uniforms at global scope,
- // so it should work out.
+ // After we have allocated registers/bindings to everything
+ // in the global scope we will process the parameters
+ // of each entry point in order.
//
- // For legacy GLSL targets, we'd probably need a distinct resource
- // kind and set of rules here, since legacy uniforms are not the
- // same as the contents of a constant buffer.
- auto globalScopeRules = context.getRulesFamily()->getConstantBufferRules();
-
- RefPtr<StructTypeLayout> globalScopeStructLayout = new StructTypeLayout();
- globalScopeStructLayout->rules = globalScopeRules;
-
- UniformLayoutInfo structLayoutInfo = globalScopeRules->BeginStructLayout();
- for( auto& parameterInfo : sharedContext.parameters )
+ // Note: the effect of the current implemetnation is to
+ // allocate non-overlapping registers/bindings between all
+ // the entry points in the compile request (e.g., if you
+ // have a vertex and fragment shader being compiled together,
+ // we will allocate distinct constant buffer registers for
+ // their uniform parameters).
+ //
+ // TODO: We probably need to provide some more nuanced control
+ // over whether entry points get overlapping or non-overlapping
+ // bindings. It seems clear that if we were compiling multiple
+ // compute kernels in one invocation we'd want them to get
+ // overlapping bindings, because we cannot ever have them bound
+ // together in a single pipeline state.
+ //
+ // Similarly, entry point parameters of DirectX Raytracing (DXR)
+ // shaders should probably be allowed to overlap by default,
+ // since those parameters should really go into the "local root signature."
+ // (Note: there is a bit more subtlety around ray tracing
+ // shaders that will be assembled into a "hit group")
+ //
+ // For now we are just doing the simplest thing, which will be
+ // appropriate for:
+ //
+ // * Compiling a single compute shader in a compile request.
+ // * Compiling some number of rasterization shader entry points
+ // in a single request, to be used together.
+ // * Compiling a single ray-tracing shader in a compile request.
+ //
+ for( auto entryPoint : sharedContext.programLayout->entryPoints )
{
- SLANG_RELEASE_ASSERT(parameterInfo->varLayouts.Count() != 0);
- auto firstVarLayout = parameterInfo->varLayouts.First();
-
- // Does the field have any uniform data?
- auto layoutInfo = firstVarLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform);
- LayoutSize uniformSize = layoutInfo ? layoutInfo->count : 0;
- if( uniformSize != 0 )
- {
- // Make sure uniform fields get laid out properly...
-
- UniformLayoutInfo fieldInfo(
- uniformSize,
- firstVarLayout->typeLayout->uniformAlignment);
-
- LayoutSize uniformOffset = globalScopeRules->AddStructField(
- &structLayoutInfo,
- fieldInfo);
-
- for( auto& varLayout : parameterInfo->varLayouts )
- {
- varLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset.getFiniteValue();
- }
- }
-
- globalScopeStructLayout->fields.Add(firstVarLayout);
-
- for( auto& varLayout : parameterInfo->varLayouts )
- {
- globalScopeStructLayout->mapVarToLayout.Add(varLayout->varDecl.getDecl(), varLayout);
- }
+ auto entryPointParamsLayout = entryPoint->parametersLayout;
+ completeBindingsForParameter(&context, entryPointParamsLayout);
}
- globalScopeRules->EndStructLayout(&structLayoutInfo);
- RefPtr<TypeLayout> globalScopeLayout = globalScopeStructLayout;
-
- // If there are global-scope uniforms, then we need to wrap
- // up a global constant buffer type layout to hold them
- if( needDefaultConstantBuffer )
+ // Next we need to create a type layout to reflect the information
+ // we have collected, and we will use the `ScopeLayoutBuilder`
+ // to encapsulate the logic that can be shared with the entry-point
+ // case.
+ //
+ ScopeLayoutBuilder globalScopeLayoutBuilder;
+ globalScopeLayoutBuilder.beginLayout(&context);
+ for( auto& parameterInfo : sharedContext.parameters )
{
- auto globalConstantBufferLayout = createParameterGroupTypeLayout(
- layoutContext,
- nullptr,
- globalScopeRules,
- globalScopeRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer),
- globalScopeStructLayout);
-
- globalScopeLayout = globalConstantBufferLayout;
+ globalScopeLayoutBuilder.addParameter(parameterInfo);
}
- // We now have a bunch of layout information, which we should
- // record into a suitable object that represents the program
- RefPtr<VarLayout> globalVarLayout = new VarLayout();
- globalVarLayout->typeLayout = globalScopeLayout;
- if (needDefaultConstantBuffer)
+ auto globalScopeVarLayout = globalScopeLayoutBuilder.endLayout();
+ if( globalConstantBufferBinding.count != 0 )
{
- auto cbInfo = globalVarLayout->findOrAddResourceInfo(LayoutResourceKind::ConstantBuffer);
+ auto cbInfo = globalScopeVarLayout->findOrAddResourceInfo(globalConstantBufferBinding.kind);
cbInfo->space = globalConstantBufferBinding.space;
cbInfo->index = globalConstantBufferBinding.index;
}
- programLayout->globalScopeLayout = globalVarLayout;
+ programLayout->parametersLayout = globalScopeVarLayout;
}
-StructTypeLayout* getGlobalStructLayout(
- ProgramLayout* programLayout);
-
RefPtr<ProgramLayout> specializeProgramLayout(
- TargetRequest * targetReq,
- ProgramLayout* programLayout,
+ TargetRequest* targetReq,
+ ProgramLayout* oldProgramLayout,
SubstitutionSet typeSubst)
{
+ // The goal of the layout specialization step is to take an existing `ProgramLayout`,
+ // and add a layout to any parameter(s) that could not be laid out previously, because
+ // they had a dependence on generic type parameters that made layout impossible at
+ // the time.
+ //
+ // TODO: It would be far simpler to just "re-do" the entire layout process, just
+ // with knowledge of what the global type substitution is, but that would mean that
+ // global parameters that come after a generic-dependent parameter might change
+ // their location/binding/register depending on what types are plugged in.
+ // Our current design preserves the layout for any global parameter that was placed during
+ // the initial layout of a program (before the generic arguments were know).
+ // It isn't clear that this design choice pays off in practice, since there is lot
+ // of complexity in this function.
+
RefPtr<ProgramLayout> newProgramLayout;
newProgramLayout = new ProgramLayout();
newProgramLayout->targetRequest = targetReq;
- newProgramLayout->globalGenericParams = programLayout->globalGenericParams;
-
- List<RefPtr<TypeLayout>> paramTypeLayouts;
- auto globalStructLayout = getGlobalStructLayout(programLayout);
- SLANG_ASSERT(globalStructLayout);
- RefPtr<StructTypeLayout> structLayout = new StructTypeLayout();
- RefPtr<TypeLayout> globalScopeLayout = structLayout;
- structLayout->uniformAlignment = globalStructLayout->uniformAlignment;
-
- // Try to find rules based on the selected code-generation target
- auto layoutContext = getInitialLayoutContextForTarget(targetReq);
+ newProgramLayout->globalGenericParams = oldProgramLayout->globalGenericParams;
- // If there was no target, or there are no rules for the target,
- // then bail out here.
- if (!layoutContext.rules)
- return newProgramLayout;
+ // The basic idea will be to iterate over the parameters in the old layout,
+ // and "pick up where we left off" in terms of allocating registers to things.
+ //
+ // That means we will look at the existing parameters (that were laid out already)
+ // and mark any registers/bytes/bindings/etc. that they occupy as "used" so
+ // that the subsequent layout of the generic-dependency parameters will not
+ // collide with them.
+ //
+ // We will use the same kind of context type as the original parameter binding
+ // step did, so we initialize its state here:
+
+ auto layoutContext = getInitialLayoutContextForTarget(targetReq);
+ SLANG_ASSERT(layoutContext.rules);
-
- // we need to initialize a layout context to mark used registers
SharedParameterBindingContext sharedContext;
sharedContext.compileRequest = targetReq->compileRequest;
sharedContext.defaultLayoutRules = layoutContext.getRulesFamily();
sharedContext.programLayout = newProgramLayout;
sharedContext.targetRequest = targetReq;
- // Create a sub-context to collect parameters that get
- // declared into the global scope
ParameterBindingContext context;
context.shared = &sharedContext;
context.translationUnit = nullptr;
context.layoutContext = layoutContext;
-
-
+
+ // We will also need state for laying out any global-scope parameters
+ // that include ordinary/uniform data.
+ //
+ auto oldGlobalStructLayout = getGlobalStructLayout(oldProgramLayout);
+ SLANG_ASSERT(oldGlobalStructLayout);
+
+ ScopeLayoutBuilder newGlobalScopeLayoutBuilder;
+ newGlobalScopeLayoutBuilder.beginLayout(&context);
+ auto& newGlobalStructLayoutInfo = newGlobalScopeLayoutBuilder.m_structLayoutInfo;
+ auto newGlobalStructLayout = newGlobalScopeLayoutBuilder.m_structLayout;
+
+ // The initial state for uniform layout will be based on whatever
+ // global-scope ordinary/uniform parameters were laid out before.
+ // The alignment can be read directly from the old global layout.
+ //
+ newGlobalStructLayoutInfo.alignment = oldGlobalStructLayout->uniformAlignment;
+ newGlobalStructLayoutInfo.size = 0;
+
+ // The remaining information needs to be collected by looking at
+ // the individual parameters in the existing layout.
+ //
+ bool oldAnyUniforms = false;
+ for(auto oldVarLayout : oldGlobalStructLayout->fields)
+ {
+ // If a parameter made use of a global generic parameter, then we would
+ // have skipped applying layout to it in the original layout process,
+ // and so we should skip it for the process of recovering the existing
+ // layout information.
+ //
+ if (oldVarLayout->FindResourceInfo(LayoutResourceKind::GenericResource))
+ continue;
+
+ // Otherwise, we will "reserve" any resources that the parameter was
+ // determined to consume.
+ //
+ // The easy case is any registers/bindings used for textures/sampler/etc.
+ // We iterate over the kinds of resources consumed by teh parameter.
+ //
+ for( auto varResInfo : oldVarLayout->resourceInfos )
+ {
+ // For each kind of resource consumed the `varResInfo` will tell us
+ // the start of the consumed range, whle the type will be needed
+ // to tell us the amount of resources consumed.
+ //
+ if( auto typeResInfo = oldVarLayout->typeLayout->FindResourceInfo(varResInfo.kind) )
+ {
+ // We will mark the range of resources consumed by theis parameter
+ // as "used" so that it cannot be claimed by later parameters.
+ //
+ auto usedRangeSet = findUsedRangeSetForSpace(&context, varResInfo.space);
+ markSpaceUsed(&context, varResInfo.space);
+ usedRangeSet->usedResourceRanges[(int)varResInfo.kind].Add(
+ nullptr, // we don't need to track parameter info here
+ varResInfo.index,
+ varResInfo.index + typeResInfo->count);
+ }
+ }
+
+ // The more subtle case is when the parameter consumes ordinary bytes
+ // of uniform (constant buffer) memory, because we do not use the
+ // same "used range" model to allocate space for ordinary data.
+ //
+ // Instead, we simply track the highest byte offset covered by any parameter.
+ //
+ if (auto varUniformInfo = oldVarLayout->FindResourceInfo(LayoutResourceKind::Uniform))
+ {
+ oldAnyUniforms = true;
+
+ if( auto typeUniformInfo = oldVarLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform) )
+ {
+ newGlobalStructLayoutInfo.size = maximum(
+ newGlobalStructLayoutInfo.size,
+ varUniformInfo->index + typeUniformInfo->count);
+ }
+ }
+ }
+
+ // Rather than attempt to re-use the entry-point layout information
+ // that was collected in the first pass, we will re-collect the
+ // information for entry points from scratch.
+ //
+ // This ensures that when an entry point makes use of a generic type
+ // parameter, the layout of its parameter list strictly follows
+ // the declaration order.
+ //
for (auto & translationUnit : targetReq->compileRequest->translationUnits)
{
for (auto & entryPoint : translationUnit->entryPoints)
@@ -2951,137 +3088,145 @@ RefPtr<ProgramLayout> specializeProgramLayout(
context.entryPointLayout = nullptr;
}
- auto constantBufferRules = context.getRulesFamily()->getConstantBufferRules();
- structLayout->rules = constantBufferRules;
- structLayout->fields.SetSize(globalStructLayout->fields.Count());
- UniformLayoutInfo structLayoutInfo;
- structLayoutInfo.alignment = globalStructLayout->uniformAlignment;
- structLayoutInfo.size = 0;
- bool anyUniforms = false;
- Dictionary<RefPtr<VarLayout>, RefPtr<VarLayout>> varLayoutMapping;
- for (uint32_t varId = 0; varId < globalStructLayout->fields.Count(); varId++)
+ // Now that we've marked thing as being used, we can make a second
+ // sweep to compute the requirements of any generic-dependent parameters.
+ //
+ // Along the way we will build up the new layout for the global-scope
+ // structure type, including the offsets of all ordinary/uniform fields.
+ //
+
+ bool newAnyUniforms = oldAnyUniforms;
+ List<RefPtr<VarLayout>> newVarLayouts;
+ Dictionary<RefPtr<VarLayout>, RefPtr<VarLayout>> mapOldLayoutToNew;
+ for(auto oldVarLayout : oldGlobalStructLayout->fields)
{
- auto &varLayout = globalStructLayout->fields[varId];
- // To recover layout context, we skip generic resources in the first pass
- if (varLayout->FindResourceInfo(LayoutResourceKind::GenericResource))
+ // In this pass, the variables that *don't* depend on generic parameters
+ // are the easy ones to handle. We can just copy them over to the new layout.
+ //
+ if(!oldVarLayout->FindResourceInfo(LayoutResourceKind::GenericResource))
+ {
+ newGlobalStructLayout->fields.Add(oldVarLayout);
continue;
+ }
- if (auto uniformInfo = varLayout->FindResourceInfo(LayoutResourceKind::Uniform))
- {
- anyUniforms = true;
+ // In the case where things are generic-dependent, we need to re-do
+ // the type layout process on the type that results from doing
+ // substutition with the global generic arguments.
+ //
+ RefPtr<Type> oldType = oldVarLayout->getTypeLayout()->getType();
+ RefPtr<Type> newType = oldType->Substitute(typeSubst).as<Type>();
- if( auto tUniformInfo = varLayout->typeLayout->FindResourceInfo(LayoutResourceKind::Uniform) )
- {
- structLayoutInfo.size = maximum(structLayoutInfo.size, uniformInfo->index + tUniformInfo->count);
- }
- }
- for( auto resInfo : varLayout->resourceInfos )
+ RefPtr<TypeLayout> newTypeLayout = getTypeLayoutForGlobalShaderParameter(
+ &context,
+ oldVarLayout->varDecl,
+ newType);
+
+ RefPtr<VarLayout> newVarLayout = new VarLayout();
+ newVarLayout->varDecl = oldVarLayout->varDecl;
+ newVarLayout->stage = oldVarLayout->stage;
+ newVarLayout->typeLayout = newTypeLayout;
+
+ newGlobalScopeLayoutBuilder.addParameter(newVarLayout);
+ newVarLayouts.Add(newVarLayout);
+ mapOldLayoutToNew.Add(oldVarLayout, newVarLayout);
+
+ if(auto uniformInfo = newTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform))
{
- if( auto tresInfo = varLayout->typeLayout->FindResourceInfo(resInfo.kind) )
+ if(uniformInfo->count != 0)
{
- auto usedRangeSet = findUsedRangeSetForSpace(&context, resInfo.space);
- markSpaceUsed(&context, resInfo.space);
- usedRangeSet->usedResourceRanges[(int)resInfo.kind].Add(
- nullptr, // we don't need to track parameter info here
- resInfo.index,
- resInfo.index + tresInfo->count);
+ newAnyUniforms = true;
+ diagnoseGlobalUniform(&sharedContext, newVarLayout->varDecl);
}
}
- structLayout->fields[varId] = varLayout;
- varLayoutMapping[varLayout] = varLayout;
}
- auto originalGlobalCBufferInfo = programLayout->globalScopeLayout->FindResourceInfo(LayoutResourceKind::ConstantBuffer);
- VarLayout::ResourceInfo globalCBufferInfo;
- globalCBufferInfo.kind = LayoutResourceKind::None;
- globalCBufferInfo.space = 0;
- globalCBufferInfo.index = 0;
- if (originalGlobalCBufferInfo)
+ auto newGlobalScopeVarLayout = newGlobalScopeLayoutBuilder.endLayout();
+
+ // We had better have made a copy of every field in the original layout.
+ //
+ SLANG_ASSERT(oldGlobalStructLayout->fields.Count() == newGlobalStructLayout->fields.Count());
+
+ // If there were no global-scope uniforms before, but there
+ // are now that we've done global substitution, then we
+ // need to allocate a global constant buffer to hold them.
+ //
+ auto newGlobalConstantBufferBinding = maybeAllocateConstantBufferBinding(&context, newAnyUniforms && !oldAnyUniforms);
+
+ // Now we need to "complete" finding for each of the new parameters,
+ // which is the step that actually allocates resource to them.
+ //
+ // Note: we don't support generic-dependent parameters with explicit bindings,
+ // so we should probably emit an error message about that in the original
+ // layout step.
+ //
+ for(auto newVarLayout : newVarLayouts)
{
- globalCBufferInfo.kind = LayoutResourceKind::ConstantBuffer;
- globalCBufferInfo.space = originalGlobalCBufferInfo->space;
- globalCBufferInfo.index = originalGlobalCBufferInfo->index;
+ completeBindingsForParameter(&context, newVarLayout);
}
- // we have the context restored, can continue to layout the generic variables now
- for (uint32_t varId = 0; varId < globalStructLayout->fields.Count(); varId++)
+
+ // One remaining missing step is that the `StructLayout` type maintains
+ // a map from variable declarations to their layouts, and in some cases
+ // multiple declarations will map to the same layout (because, e.g., the
+ // same `cbuffer` was declared in both a vertex and fragment shader file).
+ //
+ // We need to clone that remapping information over from the old program
+ // layout. This is why we created the `mapOldLayoutToNew` mapping in
+ // the preceding loop.
+ //
+ // TODO: This step would be easier if the `StructLayout::mapVarToLayout`
+ // dictionary were instead a mapping from variable declaration to the
+ // *index* of the corresponding layout in the `fields` array.
+ //
+ for(auto entry : oldGlobalStructLayout->mapVarToLayout)
{
- auto &varLayout = globalStructLayout->fields[varId];
- if (varLayout->typeLayout->FindResourceInfo(LayoutResourceKind::GenericResource))
- {
- RefPtr<Type> newType = varLayout->typeLayout->type->Substitute(typeSubst).dynamicCast<Type>();
- RefPtr<TypeLayout> newTypeLayout = CreateTypeLayout(
- layoutContext.with(constantBufferRules),
- newType);
- auto layoutInfo = newTypeLayout->FindResourceInfo(LayoutResourceKind::Uniform);
- LayoutSize uniformSize = layoutInfo ? layoutInfo->count : 0;
- if (uniformSize != 0)
- {
- if (globalCBufferInfo.kind == LayoutResourceKind::None)
- {
- // user defined a uniform via a global generic type argument
- // but we have not reserved a binding for the global uniform buffer
- UInt space = 0;
- auto usedRangeSet = findUsedRangeSetForSpace(&context, space);
- globalCBufferInfo.kind = LayoutResourceKind::ConstantBuffer;
- globalCBufferInfo.index =
- usedRangeSet->usedResourceRanges[
- (int)LayoutResourceKind::ConstantBuffer].Allocate(nullptr, 1);
- globalCBufferInfo.space = space;
- }
- }
- RefPtr<VarLayout> newVarLayout = new VarLayout();
- RefPtr<ParameterInfo> paramInfo = new ParameterInfo();
- newVarLayout->varDecl = varLayout->varDecl;
- newVarLayout->stage = varLayout->stage;
- newVarLayout->typeLayout = newTypeLayout;
- paramInfo->varLayouts.Add(newVarLayout);
- completeBindingsForParameter(&context, paramInfo);
- // update uniform layout
-
- if (uniformSize != 0)
- {
- // Make sure uniform fields get laid out properly...
- UniformLayoutInfo fieldInfo(
- uniformSize,
- newTypeLayout->uniformAlignment);
- LayoutSize uniformOffset = layoutContext.getRulesFamily()->getConstantBufferRules()->AddStructField(
- &structLayoutInfo,
- fieldInfo);
- newVarLayout->findOrAddResourceInfo(LayoutResourceKind::Uniform)->index = uniformOffset.getFiniteValue();
- anyUniforms = true;
-
- diagnoseGlobalUniform(&sharedContext, varLayout->varDecl);
- }
- structLayout->fields[varId] = newVarLayout;
- varLayoutMapping[varLayout] = newVarLayout;
- }
+ RefPtr<VarLayout> varLayout = entry.Value;
+ mapOldLayoutToNew.TryGetValue(varLayout, varLayout);
+ newGlobalStructLayout->mapVarToLayout[entry.Key] = varLayout;
}
- for (auto mapping : globalStructLayout->mapVarToLayout)
+
+ // Just as for the initial computation of layout, we will complete
+ // binding for entry-point parameters *after* we have laid out
+ // all the global-scope parameters.
+ //
+ // Note that this includes layout of generic-dependent global scope
+ // parameters, so it is possible for entry point uniform parameters
+ // to end up with a different register/binding after generic specialization.
+ // (There really isn't a great way around that)
+ //
+ for( auto entryPoint : sharedContext.programLayout->entryPoints )
{
- RefPtr<VarLayout> updatedVarLayout = mapping.Value;
- varLayoutMapping.TryGetValue(updatedVarLayout, updatedVarLayout);
- structLayout->mapVarToLayout[mapping.Key] = updatedVarLayout;
+ auto entryPointParamsLayout = entryPoint->parametersLayout;
+ completeBindingsForParameter(&context, entryPointParamsLayout);
}
- // If there are global-scope uniforms, then we need to wrap
- // up a global constant buffer type layout to hold them
- RefPtr<VarLayout> globalVarLayout = new VarLayout();
- if (anyUniforms)
+ // As a last step we need to set up the binding/offset information
+ // for the global scope itself.
+ //
+ // We will start by copying whatever information was in the old layout.
+ //
{
- auto globalConstantBufferLayout = createParameterGroupTypeLayout(
- layoutContext,
- nullptr,
- constantBufferRules,
- constantBufferRules->GetObjectLayout(ShaderParameterKind::ConstantBuffer),
- structLayout);
+ auto oldGlobalScopeVarLayout = oldProgramLayout->parametersLayout;
+ for( auto oldResInfo : oldGlobalScopeVarLayout->resourceInfos )
+ {
+ auto newResInfo = newGlobalScopeVarLayout->findOrAddResourceInfo(oldResInfo.kind);
+ newResInfo->space = oldResInfo.space;
+ newResInfo->kind = oldResInfo.kind;
+ }
+ }
- globalScopeLayout = globalConstantBufferLayout;
- auto cbInfo = globalVarLayout->findOrAddResourceInfo(LayoutResourceKind::ConstantBuffer);
- *cbInfo = globalCBufferInfo;
+ // If we had to create a constant buffer to house the global-scope
+ // ordinary/uniform data, then we need to make sure to set that
+ // information on the global scope.
+ //
+ if(newGlobalConstantBufferBinding.kind != LayoutResourceKind::None )
+ {
+ auto resInfo = newGlobalScopeVarLayout->findOrAddResourceInfo(newGlobalConstantBufferBinding.kind);
+ resInfo->space = newGlobalConstantBufferBinding.space;
+ resInfo->index = newGlobalConstantBufferBinding.index;
}
- globalVarLayout->typeLayout = globalScopeLayout;
- programLayout->globalScopeLayout = globalVarLayout;
- newProgramLayout->globalScopeLayout = globalVarLayout;
+
+ newProgramLayout->parametersLayout = newGlobalScopeVarLayout;
return newProgramLayout;
}
-}
+
+} // namespace Slang
diff --git a/source/slang/reflection.cpp b/source/slang/reflection.cpp
index 76e687483..ce42cf10d 100644
--- a/source/slang/reflection.cpp
+++ b/source/slang/reflection.cpp
@@ -1155,7 +1155,7 @@ SLANG_API unsigned spReflectionEntryPoint_getParameterCount(
auto entryPointLayout = convert(inEntryPoint);
if(!entryPointLayout) return 0;
- return getParameterCount(entryPointLayout);
+ return getParameterCount(entryPointLayout->parametersLayout->typeLayout);
}
SLANG_API SlangReflectionVariableLayout* spReflectionEntryPoint_getParameterByIndex(
@@ -1165,7 +1165,7 @@ SLANG_API SlangReflectionVariableLayout* spReflectionEntryPoint_getParameterByIn
auto entryPointLayout = convert(inEntryPoint);
if(!entryPointLayout) return 0;
- return convert(getParameterByIndex(entryPointLayout, index));
+ return convert(getParameterByIndex(entryPointLayout->parametersLayout->typeLayout, index));
}
SLANG_API SlangStage spReflectionEntryPoint_getStage(SlangReflectionEntryPoint* inEntryPoint)
@@ -1276,12 +1276,6 @@ SLANG_API SlangReflectionType* spReflectionTypeParameter_GetConstraintByIndex(Sl
// Shader Reflection
-namespace Slang
-{
- StructTypeLayout* getGlobalStructLayout(
- ProgramLayout* programLayout);
-}
-
SLANG_API unsigned spReflection_GetParameterCount(SlangReflection* inProgram)
{
auto program = convert(inProgram);
@@ -1365,7 +1359,7 @@ SLANG_API SlangUInt spReflection_getGlobalConstantBufferBinding(SlangReflection*
{
auto program = convert(inProgram);
if (!program) return 0;
- auto cb = program->globalScopeLayout->FindResourceInfo(LayoutResourceKind::ConstantBuffer);
+ auto cb = program->parametersLayout->FindResourceInfo(LayoutResourceKind::ConstantBuffer);
if (!cb) return 0;
return cb->index;
}
diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj
index 1ad408e73..b0ac37440 100644
--- a/source/slang/slang.vcxproj
+++ b/source/slang/slang.vcxproj
@@ -185,6 +185,7 @@
<ClInclude Include="ir-constexpr.h" />
<ClInclude Include="ir-dce.h" />
<ClInclude Include="ir-dominators.h" />
+ <ClInclude Include="ir-entry-point-uniforms.h" />
<ClInclude Include="ir-glsl-legalize.h" />
<ClInclude Include="ir-inst-defs.h" />
<ClInclude Include="ir-insts.h" />
@@ -240,6 +241,7 @@
<ClCompile Include="ir-constexpr.cpp" />
<ClCompile Include="ir-dce.cpp" />
<ClCompile Include="ir-dominators.cpp" />
+ <ClCompile Include="ir-entry-point-uniforms.cpp" />
<ClCompile Include="ir-glsl-legalize.cpp" />
<ClCompile Include="ir-legalize-types.cpp" />
<ClCompile Include="ir-link.cpp" />
diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters
index 9e3de4b93..0a44f9f57 100644
--- a/source/slang/slang.vcxproj.filters
+++ b/source/slang/slang.vcxproj.filters
@@ -54,6 +54,9 @@
<ClInclude Include="ir-dominators.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="ir-entry-point-uniforms.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="ir-glsl-legalize.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -215,6 +218,9 @@
<ClCompile Include="ir-dominators.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="ir-entry-point-uniforms.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="ir-glsl-legalize.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/source/slang/type-layout.h b/source/slang/type-layout.h
index da2f0e4f7..418f4684d 100644
--- a/source/slang/type-layout.h
+++ b/source/slang/type-layout.h
@@ -575,6 +575,18 @@ public:
LayoutSize tagOffset;
};
+ /// Layout for a scoped entity like a program, module, or entry point
+class ScopeLayout : public Layout
+{
+public:
+ // The layout for the parameters of this entity.
+ //
+ RefPtr<VarLayout> parametersLayout;
+};
+
+StructTypeLayout* getScopeStructLayout(
+ ScopeLayout* programLayout);
+
// Layout information for a single shader entry point
// within a program
//
@@ -584,7 +596,7 @@ public:
//
// TODO: where to store layout info for the return
// type of the function?
-class EntryPointLayout : public StructTypeLayout
+class EntryPointLayout : public ScopeLayout
{
public:
// The corresponding function declaration
@@ -617,9 +629,10 @@ public:
};
// Layout information for the global scope of a program
-class ProgramLayout : public Layout
+class ProgramLayout : public ScopeLayout
{
public:
+ /*
// We store a layout for the declarations at the global
// scope. Note that this will *either* be a single
// `StructTypeLayout` with the fields stored directly,
@@ -634,6 +647,7 @@ public:
// to store them).
//
RefPtr<VarLayout> globalScopeLayout;
+ */
// We catalog the requested entry points here,
// and any entry-point-specific parameter data
@@ -646,6 +660,9 @@ public:
TargetRequest* targetRequest = nullptr;
};
+StructTypeLayout* getGlobalStructLayout(
+ ProgramLayout* programLayout);
+
struct LayoutRulesFamilyImpl;
// A delineation of shader parameter types into fine-grained