summaryrefslogtreecommitdiffstats
path: root/source/slang/slang-parameter-binding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/slang/slang-parameter-binding.cpp')
-rw-r--r--source/slang/slang-parameter-binding.cpp550
1 files changed, 431 insertions, 119 deletions
diff --git a/source/slang/slang-parameter-binding.cpp b/source/slang/slang-parameter-binding.cpp
index a6bd52122..5cd89bd95 100644
--- a/source/slang/slang-parameter-binding.cpp
+++ b/source/slang/slang-parameter-binding.cpp
@@ -1393,68 +1393,112 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter(
String sn = semanticName.toLower();
RefPtr<TypeLayout> typeLayout;
+
+ // First we check for a system-value semantic, operating
+ // under the assumption that *any* semantic with an `SV_`
+ // or `NV_` prefix is a system value.
+ //
if (sn.startsWith("sv_")
|| sn.startsWith("nv_"))
{
- // System-value semantic.
-
- if (state.directionMask & kEntryPointParameterDirection_Output)
+ // Fragment shader color/render target outputs need to be handled
+ // specially, because they are declared with an `SV`-prefixed
+ // "system value" semantic, but in practice they are ordinary
+ // user-defined outputs.
+ //
+ // TODO: We should consider allowing fragment-shader outputs
+ // with arbitrary semantics, and simply treat them as if
+ // they were declared with `SV_Target`.
+ //
+ if( (state.directionMask & kEntryPointParameterDirection_Output)
+ && (state.stage == Stage::Fragment)
+ && (sn == "sv_target") )
{
- // Note: I'm just doing something expedient here and detecting `SV_Target`
- // outputs and claiming the appropriate register range right away.
+ // Note: For D3D shader models 5.0 and below, each `SV_Target<N>`
+ // output conflicts with UAV register `u<N>`.
//
- // TODO: we should really be building up some representation of all of this,
- // once we've gone to the trouble of looking it all up...
- if( sn == "sv_target" )
+ if( isD3DTarget(context->getTargetRequest()) )
{
- // TODO: construct a `ParameterInfo` we can use here so that
- // overlapped layout errors get reported nicely.
-
- auto usedResourceSet = findUsedRangeSetForSpace(context, 0);
- usedResourceSet->usedResourceRanges[int(LayoutResourceKind::UnorderedAccess)].Add(nullptr, semanticIndex, semanticIndex + semanticSlotCount);
-
-
- // We also need to track this as an ordinary varying output from the stage,
- // since that is how GLSL will want to see it.
- //
- typeLayout = getSimpleVaryingParameterTypeLayout(
- context->layoutContext,
- type,
- kEntryPointParameterDirection_Output);
+ auto version = context->getTargetRequest()->targetProfile.GetVersion();
+ if( version <= ProfileVersion::DX_5_0 )
+ {
+ // We will address the conflict here by claiming the corresponding
+ // `u` register.
+ //
+ // Note: because entry point parameters get processed *before*
+ // registers get assigned to global-scope parameters, this
+ // allocation will prevent register `u<N>` from being auto-assigned
+ // to any global parameter.
+ //
+ // TODO: construct a `ParameterInfo` we can use here so that
+ // overlapped layout errors get reported nicely.
+ //
+ auto usedResourceSet = findUsedRangeSetForSpace(context, 0);
+ usedResourceSet->usedResourceRanges[int(LayoutResourceKind::UnorderedAccess)].Add(nullptr, semanticIndex, semanticIndex + semanticSlotCount);
+ }
}
- }
- if (state.directionMask & kEntryPointParameterDirection_Input)
- {
- if (sn == "sv_sampleindex")
- {
- state.isSampleRate = true;
- }
+ // A fragment shader output is effectively a user-defined output,
+ // even if it was declared with `SV_Target`.
+ //
+ typeLayout = getSimpleVaryingParameterTypeLayout(
+ context->layoutContext,
+ type,
+ kEntryPointParameterDirection_Output);
}
-
- if( !typeLayout )
+ else
{
- // If we didn't compute a special-case layout for the
- // system-value parameter (e.g., because it was an
- // `SV_Target` output), then create a default layout
- // that consumes no input/output varying slots.
- // (since system parameters are distinct from
- // user-defined parameters for layout purposes)
+ // For a system-value parameter (that didn't match the
+ // `SV_Target` special case above) we create a default
+ // layout that consumes no input/output varying slots.
+ //
+ // The rationale here is that system parameters are distinct
+ // form user-defined parameters for layout purposes, and
+ // in particular should not be assigned `location`s on
+ // GLSL-based targets.
//
typeLayout = getSimpleVaryingParameterTypeLayout(
context->layoutContext,
type,
0);
+
+ // We need to compute whether an entry point consumes
+ // any sample-rate inputs, and along with explicitly
+ // `sample`-qualified parameters, we also need to
+ // detect use of `SV_SampleIndex` as an input.
+ //
+ if (state.directionMask & kEntryPointParameterDirection_Input)
+ {
+ if (sn == "sv_sampleindex")
+ {
+ state.isSampleRate = true;
+ }
+ }
}
- // Remember the system-value semantic so that we can query it later
+ // For any case of a system-value semantic (including `SV_Target`)
+ // we record the system-value semantic so it can be queried
+ // via reflection.
+ //
+ // TODO: We might want to consider skipping this step for
+ // `SV_Target` outputs and treating them consistently as
+ // just user-defined outputs.
+ //
if (varLayout)
{
varLayout->systemValueSemantic = semanticName;
varLayout->systemValueSemanticIndex = semanticIndex;
}
- // TODO: add some kind of usage information for system input/output
+ // TODO: We might want to consider tracking some kind of usage
+ // information for system inputs/outputs. In particular, it
+ // would be good to check for and diagnose overlapping system
+ // value declarations.
+
+ // TODO: We should eventually be checking that system values
+ // are appropriate to the stage that they appear on, and also
+ // map the system value semantic string over to an `enum`
+ // type of known/supported system value semantics.
}
else
{
@@ -1483,6 +1527,18 @@ static RefPtr<TypeLayout> processSimpleEntryPointParameter(
return typeLayout;
}
+ /// Compute layout information for an entry-point parameter `decl`.
+ ///
+ /// This function should be used for a top-level entry point varying
+ /// parameter or a field of a structure used for varying parameters,
+ /// but *not* for any recursive case that operates on a type without
+ /// an associated declaration (e.g., recursing on `X` when dealing
+ /// with a parameer of type `X[]`).
+ ///
+ /// This function is responsible for processing any atributes or
+ /// other modifiers on the declaration that should impact out layout
+ /// is computed.
+ ///
static RefPtr<TypeLayout> processEntryPointVaryingParameterDecl(
ParameterBindingContext* context,
Decl* decl,
@@ -1490,13 +1546,46 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameterDecl(
EntryPointParameterState const& inState,
RefPtr<VarLayout> varLayout)
{
- SimpleSemanticInfo semanticInfo;
- int semanticIndex = 0;
+ // One of our responsibilities when recursing through varying
+ // parameters is to compute the semantic name/index for each
+ // parameter.
+ //
+ // Semantics can either be declared per field/parameter:
+ //
+ // struct Output
+ // {
+ // float4 a : A;
+ // float4 b : B;
+ // }
+ //
+ // or they can be applied to an entire aggregate type:
+ //
+ // void entryPoint(out Output o : OUTPUT) { ... }
+ //
+ // When these both of the above cases apply to a
+ // leaf parameter/field, then the policy is that the
+ // "outer-most" semantic wins. Thus in the case above,
+ // `o.a` gets semantic `OUTPUT0` and `o.b` gets semantic
+ // `OUTPUT1`.
+ // By default the state we use for processing the
+ // parameter/field `decl` will be the state that was
+ // inherited from the outer context (if any).
+ //
EntryPointParameterState state = inState;
- // If there is no explicit semantic already in effect, *and* we find an explicit
- // semantic on the associated declaration, then we'll use it.
+ // If there is already a semantic name coming from the
+ // outer context, we will use it, but if there is no
+ // outer semantic *and* the current field/parameter `decl`
+ // has an explicit semantic, we will use that.
+ //
+ // Note: we allocate the storage for the variables that
+ // will track the semantic state outside the conditional,
+ // so that they are in scope for the recusrivse call
+ // coming up.
+ //
+ SimpleSemanticInfo semanticInfo;
+ int semanticIndex = 0;
if( !state.optSemanticName )
{
if( auto semantic = decl->FindModifier<HLSLSimpleSemantic>() )
@@ -1509,6 +1598,12 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameterDecl(
}
}
+ // One of our tasks is to track whether a fragment shader
+ // has any sample-rate varying inputs. To that end, we
+ // will pass down a marker if this parameter was declared
+ // with the `sample` modifier, so that we can detect
+ // sample-rate inputs at the leaves.
+ //
if (decl)
{
if (decl->FindModifier<HLSLSampleModifier>())
@@ -1517,11 +1612,89 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameterDecl(
}
}
- // Default case: either there was an explicit semantic in effect already,
- // *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 processEntryPointVaryingParameter(context, type, state, varLayout);
+ // With the state to use for assigning semantics computed,
+ // we now do processing that depends on the type of
+ // the parameter, which may involve recursing into its
+ // fields.
+ //
+ // The result of this step is the type layout to use for
+ // our field/parameter `decl` in this context.
+ //
+ auto typeLayout = processEntryPointVaryingParameter(context, type, state, varLayout);
+
+ // For Khronos targets (OpenGL and Vulkan), we need to process
+ // the `[[vk::location(...)]]` and `[[vk::index(...)]]` attributes,
+ // if present.
+ //
+ // TODO: In principle we should *also* be using the data from
+ // `SV_Target<N>` semantics as an equivalent to `location = <N>`
+ // when targetting Vulkan. Right now we are kind of skating by
+ // on the fact that people almost always declare `SV_Target`s
+ // in numerical order, so that our automatic assignment of
+ // `location`s in declaration order coincidentally matches
+ // the `SV_Target` order.
+ //
+ if( isKhronosTarget(context->getTargetRequest()) )
+ {
+ if( auto locationAttr = decl->FindModifier<GLSLLocationAttribute>() )
+ {
+ int location = locationAttr->value;
+
+ int index = 0;
+ if( auto indexAttr = decl->FindModifier<GLSLIndexAttribute>() )
+ {
+ index = indexAttr->value;
+ }
+
+ // TODO: We should eventually include validation that a non-zero
+ // `vk::index` is only valid for fragment shader color outputs.
+
+ // Once we've extracted the data from the attribute(s), we
+ // need to apply it to the `varLayout` for the parameter/field `decl`.
+ //
+ LayoutResourceKind kinds[] = { LayoutResourceKind::VaryingInput, LayoutResourceKind::VaryingOutput };
+ for( auto kind : kinds )
+ {
+ auto typeResInfo = typeLayout->FindResourceInfo(kind);
+ if(!typeResInfo)
+ continue;
+
+ auto varResInfo = varLayout->findOrAddResourceInfo(kind);
+ varResInfo->index = location;
+
+ // Note: OpenGL and Vulkan represent dual-source color blending
+ // differently from multiple render targets (MRT) at the source
+ // level.
+ //
+ // When using MRT, GLSL (and thus SPIR-V) looks like this:
+ //
+ // layout(location = 0) vec4 a;
+ // layout(location = 1) vec4 b;
+ //
+ // When using dual-source blending the GLSL/SPIR-V looks like:
+ //
+ // layout(location = 0) vec4 a;
+ // layout(location = 0, index = 1) vec4 b;
+ //
+ // Thus for a parameter of kind `VaryingOutput` when targetting
+ // GLSL/SPIR-V, we need a way to encode the value that was pased
+ // for `index` on the secondary color output.
+ //
+ // We are already using the `index` field in the `VarLayout::ResourceInfo`
+ // to store what GLSL/SPIR-V calls the "location," so we will
+ // hijack the `space` field (which is usually unused for varying
+ // parameters) to store the GLSL/SPIR-V "index" value.
+ //
+ varResInfo->space = index;
+ }
+ }
+ else if( auto indexAttr = decl->FindModifier<GLSLIndexAttribute>() )
+ {
+ getSink(context)->diagnose(indexAttr, Diagnostics::vkIndexWithoutVkLocation, decl->getName());
+ }
+ }
+
+ return typeLayout;
}
static RefPtr<TypeLayout> processEntryPointVaryingParameter(
@@ -1721,12 +1894,31 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter(
RefPtr<StructTypeLayout> structLayout = new StructTypeLayout();
structLayout->type = type;
- // Need to recursively walk the fields of the structure now...
+ // We will recursively walk the fields of a `struct` type
+ // to compute layouts for those fields.
+ //
+ // Along the way, we may find fields with explicit layout
+ // annotations, along with fields that have no explicit
+ // layout. We will consider it an error to have a mix of
+ // the two.
+ //
+ // TODO: We could support a mix of implicit and explicit
+ // layout by performing layout on fields in two passes,
+ // much like is done for the global scope. This would
+ // complicate layout significantly for little practical
+ // benefit, so it is very much a "nice to have" rather
+ // than a "must have" feature.
+ //
+ Decl* firstExplicit = nullptr;
+ Decl* firstImplicit = nullptr;
for( auto field : GetFields(structDeclRef) )
{
RefPtr<VarLayout> fieldVarLayout = new VarLayout();
fieldVarLayout->varDecl = field;
+ structLayout->fields.add(fieldVarLayout);
+ structLayout->mapVarToLayout.Add(field.getDecl(), fieldVarLayout);
+
auto fieldTypeLayout = processEntryPointVaryingParameterDecl(
context,
field.getDecl(),
@@ -1734,22 +1926,52 @@ static RefPtr<TypeLayout> processEntryPointVaryingParameter(
state,
fieldVarLayout);
- if(fieldTypeLayout)
+ SLANG_ASSERT(fieldTypeLayout);
+ if(!fieldTypeLayout)
+ continue;
+ fieldVarLayout->typeLayout = fieldTypeLayout;
+
+ // The field needs to have offset information stored
+ // in `fieldVarLayout` for every kind of resource
+ // consumed by `fieldTypeLayout`.
+ //
+ for(auto fieldTypeResInfo : fieldTypeLayout->resourceInfos)
{
- fieldVarLayout->typeLayout = fieldTypeLayout;
+ SLANG_RELEASE_ASSERT(fieldTypeResInfo.count != 0);
+ auto kind = fieldTypeResInfo.kind;
- for (auto rr : fieldTypeLayout->resourceInfos)
- {
- SLANG_RELEASE_ASSERT(rr.count != 0);
+ auto structTypeResInfo = structLayout->findOrAddResourceInfo(kind);
- auto structRes = structLayout->findOrAddResourceInfo(rr.kind);
- fieldVarLayout->findOrAddResourceInfo(rr.kind)->index = structRes->count.getFiniteValue();
- structRes->count += rr.count;
+ auto fieldResInfo = fieldVarLayout->FindResourceInfo(kind);
+ if( !fieldResInfo )
+ {
+ if(!firstImplicit) firstImplicit = field;
+
+ // In the implicit-layout case, we assign the field
+ // the next available offset after the fields that
+ // have preceded it.
+ //
+ fieldResInfo = fieldVarLayout->findOrAddResourceInfo(kind);
+ fieldResInfo->index = structTypeResInfo->count.getFiniteValue();
+ structTypeResInfo->count += fieldTypeResInfo.count;
+ }
+ else
+ {
+ if(!firstExplicit) firstExplicit = field;
+
+ // In the explicit case, the field already has offset
+ // information, and we just need to update the computed
+ // size of the `struct` type to account for the field.
+ //
+ auto fieldEndOffset = fieldResInfo->index + fieldTypeResInfo.count;
+ structTypeResInfo->count = maximum(structTypeResInfo->count, fieldEndOffset);
}
- }
- structLayout->fields.add(fieldVarLayout);
- structLayout->mapVarToLayout.Add(field.getDecl(), fieldVarLayout);
+ }
+ }
+ if( firstImplicit && firstExplicit )
+ {
+ getSink(context)->diagnose(firstImplicit, Diagnostics::mixingImplicitAndExplicitBindingForVaryingParams, firstImplicit->getName(), firstExplicit->getName());
}
return structLayout;
@@ -1992,44 +2214,6 @@ struct ScopeLayoutBuilder
}
- // Add a "simple" parameter that cannot have any user-defined
- // register or binding modifiers, so that its layout computation
- // can be simplified greatly.
- //
- void addSimpleParameter(
- RefPtr<VarLayout> varLayout)
- {
- // The main `addParameter` logic will deal with any ordinary/uniform data,
- // and with the "pending" part of the layout.
- //
- addParameter(varLayout);
-
- // That leaves us to deal with the resource usage that isn't
- // handled by `addParameter`.
- //
- auto paramTypeLayout = varLayout->getTypeLayout();
- for (auto paramTypeResInfo : paramTypeLayout->resourceInfos)
- {
- // We need to skip ordinary/uniform data because it was
- // handled by `addParameter`.
- //
- 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 scopeResInfo = m_structLayout->findOrAddResourceInfo(paramTypeResInfo.kind);
- varLayout->findOrAddResourceInfo(paramTypeResInfo.kind)->index = scopeResInfo->count.getFiniteValue();
-
- // We then need to add the resources consumed by the parameter
- // to those consumed by the scope.
- //
- scopeResInfo->count += paramTypeResInfo.count;
- }
- }
-
RefPtr<VarLayout> endLayout()
{
// Finish computing the layout for the ordindary data (if any).
@@ -2069,6 +2253,121 @@ struct ScopeLayoutBuilder
}
};
+// Scope layout builder specialized to the case of "simple"
+// scopes (more or less everything but the global scope)
+//
+struct SimpleScopeLayoutBuilder : ScopeLayoutBuilder
+{
+ typedef ScopeLayoutBuilder Super;
+
+ // Add a "simple" parameter that cannot have any user-defined
+ // register or binding modifiers, so that its layout computation
+ // can be simplified greatly.
+ //
+ void addSimpleParameter(
+ RefPtr<VarLayout> varLayout)
+ {
+ // The main `addParameter` logic will deal with any ordinary/uniform data,
+ // and with the "pending" part of the layout.
+ //
+ addParameter(varLayout);
+
+ // That leaves us to deal with the resource usage that isn't
+ // handled by `addParameter`, which we will defer until
+ // `endLayout()` is called.
+ }
+
+ RefPtr<VarLayout> endLayout()
+ {
+ // In order to support a mix of parameters with explicit
+ // and implicit layout, we will process the parameters in
+ // two phases.
+ //
+ // In the first phase we will collect information about
+ // resource ranges already claimed by parameters in the
+ // scope.
+ //
+ UsedRanges usedRangeSet[kLayoutResourceKindCount];
+ for( auto paramVarLayout : m_structLayout->fields )
+ {
+ auto paramTypeLayout = paramVarLayout->getTypeLayout();
+ for (auto paramTypeResInfo : paramTypeLayout->resourceInfos)
+ {
+ auto kind = paramTypeResInfo.kind;
+
+ // We will look for an explicit/existing binding in
+ // the parameter var layout, which would represent
+ // an explicit binding, and skip the parameter if
+ // we don't find one.
+ //
+ auto paramResInfo = paramVarLayout->FindResourceInfo(kind);
+ if(!paramResInfo)
+ continue;
+
+ // If we found an explicit binding, then we need
+ // to add it to our set for tracking.
+ //
+ auto startOffset = paramResInfo->index;
+ auto endOffset = startOffset + paramTypeResInfo.count;
+ usedRangeSet[int(kind)].Add(paramVarLayout, startOffset, endOffset);
+ }
+ }
+ //
+ // Next we iterate over the parameters again, and assign
+ // unused ranges to all of those that didn't have ranges
+ // explicitly bound.
+ //
+ for( auto paramVarLayout : m_structLayout->fields )
+ {
+ auto paramTypeLayout = paramVarLayout->getTypeLayout();
+ for (auto paramTypeResInfo : paramTypeLayout->resourceInfos)
+ {
+ auto kind = paramTypeResInfo.kind;
+
+ // We only care about parameters that are not already
+ // explicitly bound, so we will skip those that already
+ // have offset information for `kind`.
+ //
+ auto paramResInfo = paramVarLayout->FindResourceInfo(kind);
+ if(paramResInfo)
+ continue;
+ paramResInfo = paramVarLayout->findOrAddResourceInfo(kind);
+
+ paramResInfo->index = usedRangeSet[int(kind)].Allocate(paramVarLayout, paramTypeResInfo.count.getFiniteValue());
+ }
+ }
+ //
+ // Finally, we need to compute the overall resource usage of
+ // the scope/aggregate, so that it includes the ranges consumed
+ // by all of the parameters/fields.
+ //
+ for( auto paramVarLayout : m_structLayout->fields )
+ {
+ auto paramTypeLayout = paramVarLayout->getTypeLayout();
+ for (auto paramTypeResInfo : paramTypeLayout->resourceInfos)
+ {
+ auto kind = paramTypeResInfo.kind;
+
+ auto paramResInfo = paramVarLayout->FindResourceInfo(kind);
+ SLANG_ASSERT(paramResInfo);
+ if(!paramResInfo) continue;
+
+ auto startOffset = paramResInfo->index;
+ auto endOffset = startOffset + paramTypeResInfo.count;
+
+ auto scopeResInfo = m_structLayout->findOrAddResourceInfo(paramTypeResInfo.kind);
+ scopeResInfo->count = maximum(scopeResInfo->count, endOffset);
+ }
+ }
+
+ // Once we are done providing explicit offsets for all the parameters,
+ // we can defer to the base `ScopeLayoutBuilder` logic to decide
+ // whether to allocate a default constant buffer or anything like that.
+ //
+ return Super::endLayout();
+ }
+};
+
/// Helper routine to allocate a constant buffer binding if one is needed.
///
/// This function primarily exists to encapsulate the logic for allocating
@@ -2112,6 +2411,19 @@ static void removePerEntryPointParameterKinds(
typeLayout->removeResourceUsage(LayoutResourceKind::ExistentialTypeParam);
}
+static void removePerEntryPointParameterKinds(
+ VarLayout* varLayout)
+{
+ removePerEntryPointParameterKinds(varLayout->typeLayout);
+
+ varLayout->removeResourceUsage(LayoutResourceKind::VaryingInput);
+ varLayout->removeResourceUsage(LayoutResourceKind::VaryingOutput);
+ varLayout->removeResourceUsage(LayoutResourceKind::ShaderRecord);
+ varLayout->removeResourceUsage(LayoutResourceKind::HitAttributes);
+ varLayout->removeResourceUsage(LayoutResourceKind::ExistentialObjectParam);
+ varLayout->removeResourceUsage(LayoutResourceKind::ExistentialTypeParam);
+}
+
/// Iterate over the parameters of an entry point to compute its requirements.
///
static RefPtr<EntryPointLayout> collectEntryPointParameters(
@@ -2187,7 +2499,7 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
// in the parameter list (e.g., a `uniform float4x4 mvp` parameter),
// which is what the `ScopeLayoutBuilder` is designed to help with.
//
- ScopeLayoutBuilder scopeBuilder;
+ SimpleScopeLayoutBuilder scopeBuilder;
scopeBuilder.beginLayout(context);
auto paramsStructLayout = scopeBuilder.m_structLayout;
paramsStructLayout->type = entryPointType;
@@ -2255,26 +2567,6 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
//
scopeBuilder.addSimpleParameter(paramVarLayout);
}
-
- // We don't want certain kinds of resource usage within an entry
- // point to "leak" into the overall resource usage of the entry
- // point and thus lead to offsetting of successive entry points.
- //
- // For example if we have a vertex and a fragment entry point
- // in the some program, and each has one varying input, then
- // the both the vertex and fragment varying outputs should have
- // a location/index of zero. It would be bad if the fragment
- // input (or whichever entry point comes second in the global
- // ordering) started at location one, because then it wouldn't
- // line up correctly with any vertex stage outputs.
- //
- // We handle this with a bit of a kludge, by removing the
- // particular `LayoutResourceKind`s that are susceptible to
- // this problem from the overall resource usage of the entry
- // point.
- //
- removePerEntryPointParameterKinds(scopeBuilder.m_structLayout);
-
entryPointLayout->parametersLayout = scopeBuilder.endLayout();
// For an entry point with a non-`void` return type, we need to process the
@@ -2316,6 +2608,26 @@ static RefPtr<EntryPointLayout> collectEntryPointParameters(
entryPointLayout->resultLayout = resultLayout;
}
+ // We don't want certain kinds of resource usage within an entry
+ // point to "leak" into the overall resource usage of the entry
+ // point and thus lead to offsetting of successive entry points.
+ //
+ // For example if we have a vertex and a fragment entry point
+ // in the some program, and each has one varying input, then
+ // the both the vertex and fragment varying outputs should have
+ // a location/index of zero. It would be bad if the fragment
+ // input (or whichever entry point comes second in the global
+ // ordering) started at location one, because then it wouldn't
+ // line up correctly with any vertex stage outputs.
+ //
+ // We handle this with a bit of a kludge, by removing the
+ // particular `LayoutResourceKind`s that are susceptible to
+ // this problem from the overall resource usage of the entry
+ // point.
+ //
+ removePerEntryPointParameterKinds(paramsStructLayout);
+ removePerEntryPointParameterKinds(entryPointLayout->parametersLayout);
+
return entryPointLayout;
}