summaryrefslogtreecommitdiffstats
path: root/tools/gfx/vulkan/render-vk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/gfx/vulkan/render-vk.cpp')
-rw-r--r--tools/gfx/vulkan/render-vk.cpp1901
1 files changed, 1409 insertions, 492 deletions
diff --git a/tools/gfx/vulkan/render-vk.cpp b/tools/gfx/vulkan/render-vk.cpp
index b3f99ff59..a0860f0e5 100644
--- a/tools/gfx/vulkan/render-vk.cpp
+++ b/tools/gfx/vulkan/render-vk.cpp
@@ -660,28 +660,230 @@ public:
VkPipeline m_pipeline = VK_NULL_HANDLE;
};
+ // In order to bind shader parameters to the correct locations, we need to
+ // be able to describe those locations. Most shader parameters in Vulkan
+ // simply consume a single `binding`, but we also need to deal with
+ // parameters that represent push-constant ranges.
+ //
+ // In more complex cases we might be binding an entire "sub-object" like
+ // a parameter block, an entry point, etc. For the general case, we need
+ // to be able to represent a composite offset that includes offsets for
+ // each of the cases that Vulkan supports.
+
+ /// A "simple" binding offset that records `binding`, `set`, etc. offsets
+ struct SimpleBindingOffset
+ {
+ /// An offset in GLSL/SPIR-V `binding`s
+ uint32_t binding = 0;
+
+ /// The descriptor `set` that the `binding` field should be understood as an index into
+ uint32_t bindingSet = 0;
+
+ /// The starting index for any "child" descriptor sets to start at
+ uint32_t childSet = 0;
+
+ // The distinction between `bindingSet` and `childSet` above is subtle, but
+ // potentially very important when objects contain nested parameter blocks.
+ // Consider:
+ //
+ // struct Stuff { ... }
+ // struct Things
+ // {
+ // Texture2D t;
+ // ParameterBlock<Stuff> stuff;
+ // }
+ //
+ // ParameterBlock<Stuff> gStuff;
+ // Texture2D gTex;
+ // ConstantBuffer<Things> gThings;
+ //
+ // In this example, the global-scope parameters like `gTex` and `gThings`
+ // are expected to be laid out in `set=0`, and we also expect `gStuff`
+ // to be laid out as `set=1`. As a result we expect that `gThings.t`
+ // will be laid out as `binding=1,set=0` (right after `gTex`), but
+ // `gThings.stuff` should be laid out as `set=2`.
+ //
+ // In this case, when binding `gThings` we would want a binding offset
+ // that has a `binding` or 1, a `bindingSet` of 0, and a `childSet` of 2.
+ //
+ // TODO: Validate that any of this works as intended.
+
+ /// The offset in push-constant ranges (not bytes)
+ uint32_t pushConstantRange = 0;
+
+ /// Create a default (zero) offset
+ SimpleBindingOffset()
+ {}
+
+ /// Create an offset based on offset information in the given Slang `varLayout`
+ SimpleBindingOffset(slang::VariableLayoutReflection* varLayout)
+ {
+ if(varLayout)
+ {
+ bindingSet = (uint32_t) varLayout->getBindingSpace(SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);
+ binding = (uint32_t) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);
+
+ childSet = (uint32_t) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_REGISTER_SPACE);
+
+ pushConstantRange = (uint32_t) varLayout->getOffset(SLANG_PARAMETER_CATEGORY_PUSH_CONSTANT_BUFFER);
+ }
+ }
+
+ /// Add any values in the given `offset`
+ void operator+=(SimpleBindingOffset const& offset)
+ {
+ binding += offset.binding;
+ bindingSet += offset.bindingSet;
+ childSet += offset.childSet;
+ pushConstantRange += offset.pushConstantRange;
+ }
+ };
+
+ // While a "simple" binding offset representation will work in many cases,
+ // once we need to deal with layout for programs with interface-type parameters
+ // that have been statically specialized, we also need to track the offset
+ // for where to bind any "pending" data that arises from the process of static
+ // specialization.
+ //
+ // In order to conveniently track both the "primary" and "pending" offset information,
+ // we will define a more complete `BindingOffset` type that combines simple
+ // binding offsets for the primary and pending parts.
+
+ /// A representation of the offset at which to bind a shader parameter or sub-object
+ struct BindingOffset : SimpleBindingOffset
+ {
+ // Offsets for "primary" data are stored directly in the `BindingOffset`
+ // via the inheritance from `SimpleBindingOffset`.
+
+ /// Offset for any "pending" data
+ SimpleBindingOffset pending;
+
+ /// Create a default (zero) offset
+ BindingOffset()
+ {}
+
+ /// Create an offset from a simple offset
+ explicit BindingOffset(SimpleBindingOffset const& offset)
+ : SimpleBindingOffset(offset)
+ {}
+
+ /// Create an offset based on offset information in the given Slang `varLayout`
+ BindingOffset(slang::VariableLayoutReflection* varLayout)
+ : SimpleBindingOffset(varLayout)
+ , pending(varLayout->getPendingDataLayout())
+ {}
+
+ /// Add any values in the given `offset`
+ void operator+=(SimpleBindingOffset const& offset)
+ {
+ SimpleBindingOffset::operator+=(offset);
+ }
+
+ /// Add any values in the given `offset`
+ void operator+=(BindingOffset const& offset)
+ {
+ SimpleBindingOffset::operator+=(offset);
+ pending += offset.pending;
+ }
+ };
+
class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase
{
public:
+ // A shader object comprises three main kinds of state:
+ //
+ // * Zero or more bytes of ordinary ("uniform") data
+ // * Zero or more *bindings* for textures, buffers, and samplers
+ // * Zero or more *sub-objects* representing nested parameter blocks, etc.
+ //
+ // A shader object *layout* stores information that can be used to
+ // organize these different kinds of state and optimize access to them.
+ //
+ // For example, both texture/buffer/sampler bindings and sub-objects
+ // are organized into logical *binding ranges* by the Slang reflection
+ // API, and a shader object layout will store information about those
+ // ranges in a form that is usable for the Vulkan API:
+
struct BindingRangeInfo
{
slang::BindingType bindingType;
Index count;
Index baseIndex;
- Index descriptorSetIndex;
- Index rangeIndexInDescriptorSet;
- // Returns true if this binding range consumes a specialization argument slot.
- bool isSpecializationArg() const
+ /// The `binding` offset to apply for this range
+ uint32_t bindingOffset;
+
+ /// The `set` offset to apply for this range
+ uint32_t setOffset;
+
+ // Note: The 99% case is that `setOffset` will be zero. For any shader object
+ // that was allocated from an ordinary Slang type (anything other than a root
+ // shader object in fact), all of the bindings will have been allocated into
+ // a single logical descriptor set.
+ //
+ // TODO: Ideally we could refactor so that only the root shader object layout
+ // stores a set offset for its binding ranges, and all other objects skip
+ // storing a field that never actually matters.
+ };
+
+ // Sometimes we just want to iterate over the ranges that represnet
+ // sub-objects while skipping over the others, because sub-object
+ // ranges often require extra handling or more state.
+ //
+ // For that reason we also store pre-computed information about each
+ // sub-object range.
+
+ /// Offset information for a sub-object range
+ struct SubObjectRangeOffset : BindingOffset
+ {
+ SubObjectRangeOffset()
+ {}
+
+ SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout)
+ : BindingOffset(varLayout)
+ {
+ if(auto pendingLayout = varLayout->getPendingDataLayout())
+ {
+ pendingOrdinaryData = (uint32_t) pendingLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
+ }
+
+ /// The offset for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
+ };
+
+ /// Stride information for a sub-object range
+ struct SubObjectRangeStride
+ {
+ SubObjectRangeStride()
+ {}
+
+ SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout)
{
- return bindingType == slang::BindingType::ExistentialValue;
+ if(auto pendingLayout = typeLayout->getPendingDataTypeLayout())
+ {
+ pendingOrdinaryData = (uint32_t) pendingLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ }
}
+
+ /// The strid for "pending" ordinary data related to this range
+ uint32_t pendingOrdinaryData = 0;
};
+ /// Information about a logical binding range as reported by Slang reflection
struct SubObjectRangeInfo
{
- RefPtr<ShaderObjectLayoutImpl> layout;
+ /// The index of the binding range that corresponds to this sub-object range
Index bindingRangeIndex;
+
+ /// The layout expected for objects bound to this range (if known)
+ RefPtr<ShaderObjectLayoutImpl> layout;
+
+ /// The offset to use when binding the first object in this range
+ SubObjectRangeOffset offset;
+
+ /// Stride between consecutive objects in this range
+ SubObjectRangeStride stride;
};
struct DescriptorSetInfo
@@ -710,10 +912,23 @@ public:
Index m_subObjectCount = 0;
Index m_varyingInputCount = 0;
Index m_varyingOutputCount = 0;
- uint32_t m_pushConstantSize = 0;
List<DescriptorSetInfo> m_descriptorSetBuildInfos;
Dictionary<Index, Index> m_mapSpaceToDescriptorSetIndex;
+ /// The number of descriptor sets allocated by child/descendent objects
+ uint32_t m_childDescriptorSetCount = 0;
+
+ /// The total number of `binding`s consumed by this object and its children/descendents
+ uint32_t m_totalBindingCount = 0;
+
+ /// The push-constant ranges that belong to this object itself (if any)
+ List<VkPushConstantRange> m_ownPushConstantRanges;
+
+ /// The number of push-constant ranges owned by child/descendent objects
+ uint32_t m_childPushConstantRangeCount = 0;
+
+ uint32_t m_totalOrdinaryDataSize = 0;
+
Index findOrAddDescriptorSet(Index space)
{
Index index;
@@ -765,45 +980,71 @@ public:
}
}
- Result _addDescriptorSets(
- slang::TypeLayoutReflection* typeLayout,
- bool createImplicitConstantBufferForUniforms,
- slang::VariableLayoutReflection* varLayout = nullptr)
+ /// Add any descriptor ranges implied by this object containing a leaf
+ /// sub-object described by `typeLayout`, at the given `offset`.
+ void _addDescriptorRangesAsValue(
+ slang::TypeLayoutReflection* typeLayout,
+ BindingOffset const& offset)
{
- SlangInt descriptorSetCount = typeLayout->getDescriptorSetCount();
- SlangInt defaultDescriptorSetIndex;
- // If the type has ordinary uniform data fields, we need to make sure to create
- // a descriptor set with a constant buffer binding in the case that the shader
- // object is bound as a stand alone parameter block.
- uint32_t bindingOffset = 0;
- if (createImplicitConstantBufferForUniforms && typeLayout->getSize() != 0)
+ // First we will scan through all the descriptor sets that the Slang reflection
+ // information believes go into making up the given type.
+ //
+ // Note: We are initializing the sets in order so that their order in our
+ // internal data structures should be deterministically based on the order
+ // in which they are listed in Slang's reflection information.
+ //
+ Index descriptorSetCount = typeLayout->getDescriptorSetCount();
+ for (Index i = 0; i < descriptorSetCount; ++i)
{
- defaultDescriptorSetIndex = findOrAddDescriptorSet(0);
- auto& descriptorSetInfo = m_descriptorSetBuildInfos[defaultDescriptorSetIndex];
- VkDescriptorSetLayoutBinding vkBindingRangeDesc = {};
- vkBindingRangeDesc.binding = 0;
- bindingOffset = 1;
- vkBindingRangeDesc.descriptorCount = 1;
- vkBindingRangeDesc.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
- vkBindingRangeDesc.stageFlags = VK_SHADER_STAGE_ALL;
- descriptorSetInfo.vkBindings.add(vkBindingRangeDesc);
+ SlangInt descriptorRangeCount = typeLayout->getDescriptorSetDescriptorRangeCount(i);
+ if (descriptorRangeCount == 0)
+ continue;
+ auto descriptorSetIndex = findOrAddDescriptorSet(offset.bindingSet + typeLayout->getDescriptorSetSpaceOffset(i));
}
- for (SlangInt s = 0; s < descriptorSetCount; ++s)
+ // For actually populating the descriptor sets we prefer to enumerate
+ // the binding ranges of the type instead of the descriptor sets.
+ //
+ Index bindRangeCount = typeLayout->getBindingRangeCount();
+ for( Index i = 0; i < bindRangeCount; ++i )
{
- SlangInt descriptorRangeCount =
- typeLayout->getDescriptorSetDescriptorRangeCount(s);
+ auto bindingRangeIndex = i;
+ auto bindingRangeType = typeLayout->getBindingRangeType(bindingRangeIndex);
+ switch(bindingRangeType)
+ {
+ default:
+ break;
+
+ // We will skip over ranges that represent sub-objects for now, and handle
+ // them in a separate pass.
+ //
+ case slang::BindingType::ParameterBlock:
+ case slang::BindingType::ConstantBuffer:
+ case slang::BindingType::ExistentialValue:
+ case slang::BindingType::PushConstant:
+ continue;
+ }
+
+ // Given a binding range we are interested in, we will then enumerate
+ // its contained descriptor ranges.
+
+ Index descriptorRangeCount = typeLayout->getBindingRangeDescriptorRangeCount(bindingRangeIndex);
if (descriptorRangeCount == 0)
continue;
- auto descriptorSetIndex =
- findOrAddDescriptorSet(typeLayout->getDescriptorSetSpaceOffset(s));
+ auto slangDescriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(bindingRangeIndex);
+ auto descriptorSetIndex = findOrAddDescriptorSet(offset.bindingSet + typeLayout->getDescriptorSetSpaceOffset(slangDescriptorSetIndex));
auto& descriptorSetInfo = m_descriptorSetBuildInfos[descriptorSetIndex];
- for (SlangInt r = 0; r < descriptorRangeCount; ++r)
+
+ Index firstDescriptorRangeIndex = typeLayout->getBindingRangeFirstDescriptorRangeIndex(bindingRangeIndex);
+ for(Index j = 0; j < descriptorRangeCount; ++j)
{
- auto slangBindingType =
- typeLayout->getDescriptorSetDescriptorRangeType(s, r);
+ Index descriptorRangeIndex = firstDescriptorRangeIndex + j;
+ auto slangDescriptorType = typeLayout->getDescriptorSetDescriptorRangeType(slangDescriptorSetIndex, descriptorRangeIndex);
- switch (slangBindingType)
+ // Certain kinds of descriptor ranges reflected by Slang do not
+ // manifest as descriptors at the Vulkan level, so we will skip those.
+ //
+ switch (slangDescriptorType)
{
case slang::BindingType::ExistentialValue:
case slang::BindingType::InlineUniformData:
@@ -813,50 +1054,183 @@ public:
break;
}
- auto vkDescriptorType = _mapDescriptorType(slangBindingType);
+ auto vkDescriptorType = _mapDescriptorType(slangDescriptorType);
VkDescriptorSetLayoutBinding vkBindingRangeDesc = {};
- vkBindingRangeDesc.binding = bindingOffset +
- (uint32_t)typeLayout->getDescriptorSetDescriptorRangeIndexOffset(s, r);
- vkBindingRangeDesc.descriptorCount =
- (uint32_t)typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(
- s, r);
+ vkBindingRangeDesc.binding = offset.binding + (uint32_t)typeLayout->getDescriptorSetDescriptorRangeIndexOffset(slangDescriptorSetIndex, descriptorRangeIndex);
+ vkBindingRangeDesc.descriptorCount = (uint32_t)typeLayout->getDescriptorSetDescriptorRangeDescriptorCount(slangDescriptorSetIndex, descriptorRangeIndex);
vkBindingRangeDesc.descriptorType = vkDescriptorType;
vkBindingRangeDesc.stageFlags = VK_SHADER_STAGE_ALL;
- if (varLayout)
+
+ descriptorSetInfo.vkBindings.add(vkBindingRangeDesc);
+ }
+ }
+
+ // We skipped over the sub-object ranges when adding descriptors above,
+ // and now we will address that oversight by iterating over just
+ // the sub-object ranges.
+ //
+ Index subObjectRangeCount = typeLayout->getSubObjectRangeCount();
+ for(Index subObjectRangeIndex = 0; subObjectRangeIndex < subObjectRangeCount; ++subObjectRangeIndex)
+ {
+ auto bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(subObjectRangeIndex);
+ auto bindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
+
+ auto subObjectTypeLayout = typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
+ SLANG_ASSERT(subObjectTypeLayout);
+
+ BindingOffset subObjectRangeOffset = offset;
+ subObjectRangeOffset += BindingOffset(typeLayout->getSubObjectRangeOffset(subObjectRangeIndex));
+
+ switch(bindingType)
+ {
+ // A `ParameterBlock<X>` never contributes descripto ranges to the
+ // decriptor sets of a parent object.
+ //
+ case slang::BindingType::ParameterBlock:
+ default:
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ // An interest/existential-typed sub-object range will only contribute descriptor
+ // ranges to a parent object in the case where it has been specialied, which
+ // is precisely the case where the Slang reflection information will tell us
+ // about its "pending" layout.
+ //
+ if(auto pendingTypeLayout = subObjectTypeLayout->getPendingDataTypeLayout())
{
- auto category =
- typeLayout->getDescriptorSetDescriptorRangeCategory(s, r);
- vkBindingRangeDesc.binding += (uint32_t)varLayout->getOffset(category);
+ BindingOffset pendingOffset = BindingOffset(subObjectRangeOffset.pending);
+ _addDescriptorRangesAsValue(pendingTypeLayout, pendingOffset);
}
- descriptorSetInfo.vkBindings.add(vkBindingRangeDesc);
+ break;
+
+ case slang::BindingType::ConstantBuffer:
+ {
+ // A `ConstantBuffer<X>` range will contribute any nested descriptor
+ // ranges in `X`, along with a leading descriptor range for a
+ // uniform buffer to hold ordinary/uniform data, if there is any.
+
+ SLANG_ASSERT(subObjectTypeLayout);
+
+ auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout();
+ SLANG_ASSERT(containerVarLayout);
+
+ auto elementVarLayout = subObjectTypeLayout->getElementVarLayout();
+ SLANG_ASSERT(elementVarLayout);
+
+ auto elementTypeLayout = elementVarLayout->getTypeLayout();
+ SLANG_ASSERT(elementTypeLayout);
+
+ BindingOffset containerOffset = subObjectRangeOffset;
+ containerOffset += BindingOffset(subObjectTypeLayout->getContainerVarLayout());
+
+ BindingOffset elementOffset = subObjectRangeOffset;
+ elementOffset += BindingOffset(elementVarLayout);
+
+ _addDescriptorRangesAsConstantBuffer(elementTypeLayout, containerOffset, elementOffset);
+ }
+ break;
+
+ case slang::BindingType::PushConstant:
+ {
+ // This case indicates a `ConstantBuffer<X>` that was marked as being
+ // used for push constants.
+ //
+ // Much of the handling is the same as for an ordinary `ConstantBuffer<X>`,
+ // but of course we need to handle the ordinary data part differently.
+
+ SLANG_ASSERT(subObjectTypeLayout);
+
+ auto containerVarLayout = subObjectTypeLayout->getContainerVarLayout();
+ SLANG_ASSERT(containerVarLayout);
+
+ auto elementVarLayout = subObjectTypeLayout->getElementVarLayout();
+ SLANG_ASSERT(elementVarLayout);
+
+ auto elementTypeLayout = elementVarLayout->getTypeLayout();
+ SLANG_ASSERT(elementTypeLayout);
+
+ BindingOffset containerOffset = subObjectRangeOffset;
+ containerOffset += BindingOffset(subObjectTypeLayout->getContainerVarLayout());
+
+ BindingOffset elementOffset = subObjectRangeOffset;
+ elementOffset += BindingOffset(elementVarLayout);
+
+ _addDescriptorRangesAsPushConstantBuffer(elementTypeLayout, containerOffset, elementOffset);
+ }
+ break;
}
+
}
- return SLANG_OK;
}
- Result setElementTypeLayout(
- slang::TypeLayoutReflection* typeLayout,
- bool buildDescriptorSetLayout)
+ /// Add the descriptor ranges implied by a `ConstantBuffer<X>` where `X` is
+ /// described by `elementTypeLayout`.
+ ///
+ /// The `containerOffset` and `elementOffset` are the binding offsets that
+ /// should apply to the buffer itself and the contents of the buffer, respectively.
+ ///
+ void _addDescriptorRangesAsConstantBuffer(
+ slang::TypeLayoutReflection* elementTypeLayout,
+ BindingOffset const& containerOffset,
+ BindingOffset const& elementOffset)
{
- // First we will use the Slang layout information to allocate
- // the descriptor set layout(s) required to store values
- // of the given type.
- //
- if (buildDescriptorSetLayout)
+ // If the type has ordinary uniform data fields, we need to make sure to create
+ // a descriptor set with a constant buffer binding in the case that the shader
+ // object is bound as a stand alone parameter block.
+ if (elementTypeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0)
{
- SLANG_RETURN_ON_FAIL(_addDescriptorSets(typeLayout, true));
+ auto descriptorSetIndex = findOrAddDescriptorSet(containerOffset.bindingSet);
+ auto& descriptorSetInfo = m_descriptorSetBuildInfos[descriptorSetIndex];
+ VkDescriptorSetLayoutBinding vkBindingRangeDesc = {};
+ vkBindingRangeDesc.binding = containerOffset.binding;
+ vkBindingRangeDesc.descriptorCount = 1;
+ vkBindingRangeDesc.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ vkBindingRangeDesc.stageFlags = VK_SHADER_STAGE_ALL;
+ descriptorSetInfo.vkBindings.add(vkBindingRangeDesc);
}
- typeLayout = _unwrapParameterGroups(typeLayout);
+ _addDescriptorRangesAsValue(elementTypeLayout, elementOffset);
+ }
- m_elementTypeLayout = typeLayout;
+ /// Add the descriptor ranges implied by a `PushConstantBuffer<X>` where `X` is
+ /// described by `elementTypeLayout`.
+ ///
+ /// The `containerOffset` and `elementOffset` are the binding offsets that
+ /// should apply to the buffer itself and the contents of the buffer, respectively.
+ ///
+ void _addDescriptorRangesAsPushConstantBuffer(
+ slang::TypeLayoutReflection* elementTypeLayout,
+ BindingOffset const& containerOffset,
+ BindingOffset const& elementOffset)
+ {
+ // If the type has ordinary uniform data fields, we need to make sure to create
+ // a descriptor set with a constant buffer binding in the case that the shader
+ // object is bound as a stand alone parameter block.
+ auto ordinaryDataSize = (uint32_t) elementTypeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
+ if (ordinaryDataSize != 0)
+ {
+ auto pushConstantRangeIndex = containerOffset.pushConstantRange;
+ VkPushConstantRange vkPushConstantRange = {};
+ vkPushConstantRange.size = ordinaryDataSize;
+ vkPushConstantRange.stageFlags = VK_SHADER_STAGE_ALL; // TODO: be more clever
- // Next we will compute the binding ranges that are used to store
- // the logical contents of the object in memory. These will relate
- // to the descriptor ranges in the various sets, but not always
- // in a one-to-one fashion.
+ while(m_ownPushConstantRanges.getCount() <= pushConstantRangeIndex)
+ {
+ VkPushConstantRange emptyRange = { 0 };
+ m_ownPushConstantRanges.add(emptyRange);
+ }
+
+ m_ownPushConstantRanges[pushConstantRangeIndex] = vkPushConstantRange;
+ }
+
+ _addDescriptorRangesAsValue(elementTypeLayout, elementOffset);
+ }
+ /// Add binding ranges to this shader object layout, as implied by the given `typeLayout`
+ void addBindingRanges(
+ slang::TypeLayoutReflection* typeLayout)
+ {
SlangInt bindingRangeCount = typeLayout->getBindingRangeCount();
for (SlangInt r = 0; r < bindingRangeCount; ++r)
{
@@ -865,10 +1239,6 @@ public:
slang::TypeLayoutReflection* slangLeafTypeLayout =
typeLayout->getBindingRangeLeafTypeLayout(r);
- SlangInt descriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(r);
- SlangInt rangeIndexInDescriptorSet =
- typeLayout->getBindingRangeFirstDescriptorRangeIndex(r);
-
Index baseIndex = 0;
switch (slangBindingType)
{
@@ -882,11 +1252,13 @@ public:
case slang::BindingType::Sampler:
baseIndex = m_samplerCount;
m_samplerCount += count;
+ m_totalBindingCount += 1;
break;
case slang::BindingType::CombinedTextureSampler:
baseIndex = m_combinedTextureSamplerCount;
m_combinedTextureSamplerCount += count;
+ m_totalBindingCount += 1;
break;
case slang::BindingType::VaryingInput:
@@ -901,6 +1273,7 @@ public:
default:
baseIndex = m_resourceViewCount;
m_resourceViewCount += count;
+ m_totalBindingCount += 1;
break;
}
@@ -908,8 +1281,32 @@ public:
bindingRangeInfo.bindingType = slangBindingType;
bindingRangeInfo.count = count;
bindingRangeInfo.baseIndex = baseIndex;
- bindingRangeInfo.descriptorSetIndex = descriptorSetIndex;
- bindingRangeInfo.rangeIndexInDescriptorSet = rangeIndexInDescriptorSet;
+
+ // We'd like to extract the information on the GLSL/SPIR-V
+ // `binding` that this range should bind into (or whatever
+ // other specific kind of offset/index is appropriate to it).
+ //
+ // A binding range represents a logical member of the shader
+ // object type, and it may encompass zero or more *descriptor
+ // ranges* that describe how it is physically bound to pipeline
+ // state.
+ //
+ // If the current bindign range is backed by at least one descriptor
+ // range then we can query the binding offset of that descriptor
+ // range. We expect that in the common case there will be exactly
+ // one descriptor range, and we can extract the information easily.
+ //
+ if(typeLayout->getBindingRangeDescriptorRangeCount(r) != 0)
+ {
+ SlangInt descriptorSetIndex = typeLayout->getBindingRangeDescriptorSetIndex(r);
+ SlangInt descriptorRangeIndex = typeLayout->getBindingRangeFirstDescriptorRangeIndex(r);
+
+ auto set = typeLayout->getDescriptorSetSpaceOffset(descriptorSetIndex);
+ auto bindingOffset = typeLayout->getDescriptorSetDescriptorRangeIndexOffset(descriptorSetIndex, descriptorRangeIndex);
+
+ bindingRangeInfo.setOffset = uint32_t(set);
+ bindingRangeInfo.bindingOffset = uint32_t(bindingOffset);
+ }
m_bindingRanges.add(bindingRangeInfo);
}
@@ -918,6 +1315,7 @@ public:
for (SlangInt r = 0; r < subObjectRangeCount; ++r)
{
SlangInt bindingRangeIndex = typeLayout->getSubObjectRangeBindingRangeIndex(r);
+ auto& bindingRange = m_bindingRanges[bindingRangeIndex];
auto slangBindingType = typeLayout->getBindingRangeType(bindingRangeIndex);
slang::TypeLayoutReflection* slangLeafTypeLayout =
typeLayout->getBindingRangeLeafTypeLayout(bindingRangeIndex);
@@ -930,20 +1328,103 @@ public:
// know the appropraite type/layout of sub-object to allocate.
//
RefPtr<ShaderObjectLayoutImpl> subObjectLayout;
- if (slangBindingType != slang::BindingType::ExistentialValue)
+ switch(slangBindingType)
{
- ShaderObjectLayoutImpl::createForElementType(
- m_renderer,
- slangLeafTypeLayout->getElementTypeLayout(),
- true,
- subObjectLayout.writeRef());
+ default:
+ {
+ auto elementTypeLayout = slangLeafTypeLayout->getElementTypeLayout();
+ ShaderObjectLayoutImpl::createForElementType(
+ m_renderer,
+ elementTypeLayout,
+ subObjectLayout.writeRef());
+ }
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ if(auto pendingTypeLayout = slangLeafTypeLayout->getPendingDataTypeLayout())
+ {
+ ShaderObjectLayoutImpl::createForElementType(
+ m_renderer,
+ pendingTypeLayout,
+ subObjectLayout.writeRef());
+ }
+ break;
}
SubObjectRangeInfo subObjectRange;
subObjectRange.bindingRangeIndex = bindingRangeIndex;
subObjectRange.layout = subObjectLayout;
+
+ // We will use Slang reflection infromation to extract the offset information
+ // for each sub-object range.
+ //
+ // TODO: We should also be extracting the uniform offset here.
+ //
+ subObjectRange.offset = SubObjectRangeOffset(typeLayout->getSubObjectRangeOffset(r));
+ subObjectRange.stride = SubObjectRangeStride(slangLeafTypeLayout);
+
+ switch(slangBindingType)
+ {
+ case slang::BindingType::ParameterBlock:
+ m_childDescriptorSetCount += subObjectLayout->getTotalDescriptorSetCount();
+ m_childPushConstantRangeCount += subObjectLayout->getTotalPushConstantRangeCount();
+ break;
+
+ case slang::BindingType::ConstantBuffer:
+ m_childDescriptorSetCount += subObjectLayout->getChildDescriptorSetCount();
+ m_totalBindingCount += subObjectLayout->getTotalBindingCount();
+ m_childPushConstantRangeCount += subObjectLayout->getTotalPushConstantRangeCount();
+ break;
+
+ case slang::BindingType::ExistentialValue:
+ if(subObjectLayout)
+ {
+ m_childDescriptorSetCount += subObjectLayout->getChildDescriptorSetCount();
+ m_totalBindingCount += subObjectLayout->getTotalBindingCount();
+ m_childPushConstantRangeCount += subObjectLayout->getTotalPushConstantRangeCount();
+
+ // An interface-type range that includes ordinary data can
+ // increase the size of the ordinary data buffer we need to
+ // allocate for the parent object.
+ //
+ uint32_t ordinaryDataEnd = subObjectRange.offset.pendingOrdinaryData
+ + (uint32_t) bindingRange.count * subObjectRange.stride.pendingOrdinaryData;
+
+ if(ordinaryDataEnd > m_totalOrdinaryDataSize)
+ {
+ m_totalOrdinaryDataSize = ordinaryDataEnd;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
m_subObjectRanges.add(subObjectRange);
}
+ }
+
+ Result setElementTypeLayout(
+ slang::TypeLayoutReflection* typeLayout)
+ {
+ typeLayout = _unwrapParameterGroups(typeLayout);
+ m_elementTypeLayout = typeLayout;
+
+ m_totalOrdinaryDataSize = (uint32_t) typeLayout->getSize();
+
+ // Next we will compute the binding ranges that are used to store
+ // the logical contents of the object in memory. These will relate
+ // to the descriptor ranges in the various sets, but not always
+ // in a one-to-one fashion.
+
+ addBindingRanges(typeLayout);
+
+ // Note: This routine does not take responsibility for
+ // adding descriptor ranges at all, because the exact way
+ // that descriptor ranges need to be added varies between
+ // ordinary shader objects, root shader objects, and entry points.
+
return SLANG_OK;
}
@@ -960,11 +1441,53 @@ public:
static Result createForElementType(
VKDevice* renderer,
slang::TypeLayoutReflection* elementType,
- bool createConstantBufferForOrdinaryData,
ShaderObjectLayoutImpl** outLayout)
{
Builder builder(renderer);
- builder.setElementTypeLayout(elementType, createConstantBufferForOrdinaryData);
+ builder.setElementTypeLayout(elementType);
+
+ // When constructing a shader object layout directly from a reflected
+ // type in Slang, we want to compute the descriptor sets and ranges
+ // that would be used if this object were bound as a parameter block.
+ //
+ // It might seem like we need to deal with the other cases for how
+ // the shader object might be bound, but the descriptor ranges we
+ // compute here will only ever be used in parameter-block case.
+ //
+ // One important wrinkle is that we know that the parameter block
+ // allocated for `elementType` will potentially need a buffer `binding`
+ // for any ordinary data it contains.
+
+ bool needsOrdinaryDataBuffer = elementType->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0;
+ uint32_t ordinaryDataBufferCount = needsOrdinaryDataBuffer ? 1 : 0;
+
+ // When binding the object, we know that the ordinary data buffer will
+ // always use a the first available `binding`, so its offset will be
+ // all zeroes.
+ //
+ BindingOffset containerOffset;
+
+ // In contrast, the `binding`s used by all the other entries in the
+ // parameter block will need to be offset by one if there was
+ // an ordinary data buffer.
+ //
+ BindingOffset elementOffset;
+ elementOffset.binding = ordinaryDataBufferCount;
+
+ // Furthermore, any `binding`s that arise due to "pending" data
+ // in the type of the object (due to specialization for existential types)
+ // will need to come after all the other `binding`s that were
+ // part of the "primary" (unspecialized) data.
+ //
+ uint32_t primaryDescriptorCount = ordinaryDataBufferCount
+ + (uint32_t) elementType->getSize(SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT);
+ elementOffset.pending.binding = primaryDescriptorCount;
+
+ // Once we've computed the offset information, we simply add the
+ // descriptor ranges as if things were declared as a `ConstantBuffer<X>`,
+ // since that is how things will be laid out inside the parameter block.
+ //
+ builder._addDescriptorRangesAsConstantBuffer(elementType, containerOffset, elementOffset);
return builder.build(outLayout);
}
@@ -977,9 +1500,55 @@ public:
}
}
- List<DescriptorSetInfo> const& getDescriptorSets() { return m_descriptorSetInfos; }
+ /// Get the number of descriptor sets that are allocated for this object itself
+ /// (if it needed to be bound as a parameter block).
+ ///
+ uint32_t getOwnDescriptorSetCount() { return uint32_t(m_descriptorSetInfos.getCount()); }
+
+ /// Get information about the descriptor sets that would be allocated to
+ /// represent this object itself as a parameter block.
+ ///
+ List<DescriptorSetInfo> const& getOwnDescriptorSets() { return m_descriptorSetInfos; }
+
+ /// Get the number of descriptor sets that would need to be allocated and bound
+ /// to represent the children of this object if it were bound as a parameter
+ /// block.
+ ///
+ /// To a first approximation, this is the number of (transitive) children
+ /// that are declared as `ParameterBlock<X>`.
+ ///
+ uint32_t getChildDescriptorSetCount() { return m_childDescriptorSetCount; }
+
+ /// Get the total number of descriptor sets that would need to be allocated and bound
+ /// to represent this object and its children (transitively) as a parameter block.
+ ///
+ uint32_t getTotalDescriptorSetCount() { return getOwnDescriptorSetCount() + getChildDescriptorSetCount(); }
+
+ /// Get the total number of `binding`s required to represent this type and its
+ /// (transitive) children.
+ ///
+ /// Note that this count does *not* include bindings that would be part of child
+ /// parameter blocks, nor does it include the binding for an ordinary data buffer,
+ /// if one is needed.
+ ///
+ uint32_t getTotalBindingCount() { return m_totalBindingCount; }
+
- uint32_t getPushConstantSize() { return m_pushConstantSize; }
+ /// Get the list of push constant ranges required to bind the state of this object itself.
+ List<VkPushConstantRange> const& getOwnPushConstantRanges() const { return m_ownPushConstantRanges; }
+
+ /// Get the number of push constant ranges required to bind the state of this object itself.
+ uint32_t getOwnPushConstantRangeCount() { return (uint32_t) m_ownPushConstantRanges.getCount(); }
+
+ /// Get the number of push constant ranges required to bind the state of the (transitive)
+ /// children of this object.
+ uint32_t getChildPushConstantRangeCount() { return m_childPushConstantRangeCount; }
+
+ /// Get the total number of push constant ranges required to bind the state of this object
+ /// and its (transitive) children.
+ uint32_t getTotalPushConstantRangeCount() { return getOwnPushConstantRangeCount() + getChildPushConstantRangeCount(); }
+
+ uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }
List<BindingRangeInfo> const& getBindingRanges() { return m_bindingRanges; }
@@ -1014,12 +1583,15 @@ public:
m_bindingRanges = builder->m_bindingRanges;
m_descriptorSetInfos = _Move(builder->m_descriptorSetBuildInfos);
- m_pushConstantSize = builder->m_pushConstantSize;
+ m_ownPushConstantRanges = builder->m_ownPushConstantRanges;
m_resourceViewCount = builder->m_resourceViewCount;
m_samplerCount = builder->m_samplerCount;
m_combinedTextureSamplerCount = builder->m_combinedTextureSamplerCount;
+ m_childDescriptorSetCount = builder->m_childDescriptorSetCount;
+ m_totalBindingCount = builder->m_totalBindingCount;
m_subObjectCount = builder->m_subObjectCount;
m_subObjectRanges = builder->m_subObjectRanges;
+ m_totalOrdinaryDataSize = builder->m_totalOrdinaryDataSize;
// Create VkDescriptorSetLayout for all descriptor sets.
for (auto& descriptorSetInfo : m_descriptorSetInfos)
@@ -1042,7 +1614,13 @@ public:
Index m_samplerCount = 0;
Index m_combinedTextureSamplerCount = 0;
Index m_subObjectCount = 0;
- uint32_t m_pushConstantSize = 0;
+ List<VkPushConstantRange> m_ownPushConstantRanges;
+ uint32_t m_childPushConstantRangeCount = 0;
+
+ uint32_t m_childDescriptorSetCount = 0;
+ uint32_t m_totalBindingCount = 0;
+ uint32_t m_totalOrdinaryDataSize = 0;
+
List<SubObjectRangeInfo> m_subObjectRanges;
};
@@ -1069,15 +1647,18 @@ public:
void addEntryPointParams(slang::EntryPointLayout* entryPointLayout)
{
m_slangEntryPointLayout = entryPointLayout;
- setElementTypeLayout(entryPointLayout->getTypeLayout(), false);
- m_pushConstantSize = (uint32_t)_unwrapParameterGroups(entryPointLayout->getTypeLayout())
- ->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM);
- m_stage = VulkanUtil::getShaderStage(entryPointLayout->getStage());
+ setElementTypeLayout(entryPointLayout->getTypeLayout());
+ m_shaderStageFlag = VulkanUtil::getShaderStage(entryPointLayout->getStage());
+
+ // Note: we do not bother adding any descriptor sets/ranges here,
+ // because the descriptor ranges of an entry point will simply
+ // be allocated as part of the descriptor sets for the root
+ // shader object.
}
slang::EntryPointLayout* m_slangEntryPointLayout = nullptr;
- VkShaderStageFlags m_stage;
+ VkShaderStageFlags m_shaderStageFlag;
};
Result _init(Builder const* builder)
@@ -1087,16 +1668,16 @@ public:
SLANG_RETURN_ON_FAIL(Super::_init(builder));
m_slangEntryPointLayout = builder->m_slangEntryPointLayout;
- m_stage = builder->m_stage;
+ m_shaderStageFlag = builder->m_shaderStageFlag;
return SLANG_OK;
}
- VkShaderStageFlags getStage() const { return m_stage; }
+ VkShaderStageFlags getShaderStageFlag() const { return m_shaderStageFlag; }
slang::EntryPointLayout* getSlangLayout() const { return m_slangEntryPointLayout; };
slang::EntryPointLayout* m_slangEntryPointLayout;
- VkShaderStageFlags m_stage;
+ VkShaderStageFlags m_shaderStageFlag;
};
class RootShaderObjectLayout : public ShaderObjectLayoutImpl
@@ -1113,10 +1694,14 @@ public:
}
}
+ /// Information stored for each entry point of the program
struct EntryPointInfo
{
+ /// Layout of the entry point
RefPtr<EntryPointLayout> layout;
- Index rangeOffset;
+
+ /// Offset for binding the entry point, relative to the start of the program
+ BindingOffset offset;
};
struct Builder : Super::Builder
@@ -1140,34 +1725,74 @@ public:
void addGlobalParams(slang::VariableLayoutReflection* globalsLayout)
{
- setElementTypeLayout(globalsLayout->getTypeLayout(), true);
+ setElementTypeLayout(globalsLayout->getTypeLayout());
+
+ // We need to populate our descriptor sets/ranges with information
+ // from the layout of the global scope.
+ //
+ // While we expect that the parameter in the global scope start
+ // at an offset of zero, it is also worth querying the offset
+ // information because it could impact the locations assigned
+ // to "pending" data in the case of static specialization.
+ //
+ BindingOffset offset(globalsLayout);
+
+ // Note: We are adding descriptor ranges here based directly on
+ // the type of the global-scope layout. The type layout for the
+ // global scope will either be something like a `struct GlobalParams`
+ // that contains all the global-scope parameters or a `ConstantBuffer<GlobalParams>`
+ // and in either case the `_addDescriptorRangesAsValue` can properly
+ // add all the ranges implied.
+ //
+ // As a result we don't require any special-case logic here to
+ // deal with the possibility of a "default" constant buffer allocated
+ // for global-scope parameters of uniform/ordinary type.
+ //
+ _addDescriptorRangesAsValue(globalsLayout->getTypeLayout(), offset);
+
+ // We want to keep track of the offset that was applied to "pending"
+ // data because we will need it again later when it comes time to
+ // actually bind things.
+ //
+ m_pendingDataOffset = offset.pending;
}
void addEntryPoint(EntryPointLayout* entryPointLayout)
{
+ auto slangEntryPointLayout = entryPointLayout->getSlangLayout();
+ auto entryPointVarLayout = slangEntryPointLayout->getVarLayout();
+
+ // The offset information for each entry point needs to
+ // be adjusted by any offset for "pending" data that
+ // was recorded in the global-scope layout.
+ //
+ // TODO(tfoley): Double-check that this is correct.
+
+ BindingOffset entryPointOffset(entryPointVarLayout);
+ entryPointOffset.pending += m_pendingDataOffset;
+
EntryPointInfo info;
info.layout = entryPointLayout;
+ info.offset = entryPointOffset;
+
+ // Similar to the case for the global scope, we expect the
+ // type layout for the entry point parameters to be either
+ // a `struct EntryPointParams` or a `PushConstantBuffer<EntryPointParams>`.
+ // Rather than deal with the different cases here, we will
+ // trust the `_addDescriptorRangesAsValue` code to handle
+ // either case correctly.
+ //
+ _addDescriptorRangesAsValue(entryPointVarLayout->getTypeLayout(), entryPointOffset);
- if (m_descriptorSetBuildInfos.getCount())
- {
- info.rangeOffset = m_descriptorSetBuildInfos[0].vkBindings.getCount();
- }
- else
- {
- info.rangeOffset = 0;
- }
-
- auto slangEntryPointLayout = entryPointLayout->getSlangLayout();
- _addDescriptorSets(
- _unwrapParameterGroups(slangEntryPointLayout->getTypeLayout()),
- false,
- slangEntryPointLayout->getVarLayout());
m_entryPoints.add(info);
}
slang::IComponentType* m_program;
slang::ProgramLayout* m_programLayout;
List<EntryPointInfo> m_entryPoints;
+
+ /// Offset to apply to "pending" data from this object, sub-objects, and entry points
+ SimpleBindingOffset m_pendingDataOffset;
};
Index findEntryPointIndex(VkShaderStageFlags stage)
@@ -1176,7 +1801,7 @@ public:
for (Index i = 0; i < entryPointCount; ++i)
{
auto entryPoint = m_entryPoints[i];
- if (entryPoint.layout->getStage() == stage)
+ if (entryPoint.layout->getShaderStageFlag() == stage)
return i;
}
return -1;
@@ -1214,9 +1839,14 @@ public:
return SLANG_OK;
}
+ SimpleBindingOffset const& getPendingDataOffset() const { return m_pendingDataOffset; }
+
slang::IComponentType* getSlangProgram() const { return m_program; }
slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }
+ /// Get all of the push constant ranges that will be bound for this object and all (transitive) sub-objects
+ List<VkPushConstantRange> const& getAllPushConstantRanges() { return m_allPushConstantRanges; }
+
protected:
Result _init(Builder const* builder)
{
@@ -1227,75 +1857,156 @@ public:
m_program = builder->m_program;
m_programLayout = builder->m_programLayout;
m_entryPoints = _Move(builder->m_entryPoints);
+ m_pendingDataOffset = builder->m_pendingDataOffset;
m_renderer = renderer;
+ // If the program has unbound specialization parameters,
+ // then we will avoid creating a final Vulkan pipeline layout.
+ //
+ // TODO: We should really create the information necessary
+ // for binding as part of a separate object, so that we have
+ // a clean seperation between what is needed for writing into
+ // a shader object vs. what is needed for binding it to the
+ // pipeline. We eventually need to be able to create bindable
+ // state objects from unspecialized programs, in order to
+ // support dynamic dispatch.
+ //
if (m_program->getSpecializationParamCount() != 0)
return SLANG_OK;
- // For fully specialized shader programs, we create a Vulkan pipeline layout now.
+ // Otherwise, we need to create a final (bindable) layout.
+ //
+ // We will use a recursive walk to collect all the `VkDescriptorSetLayout`s
+ // that are required for the global scope, sub-objects, and entry points.
+ //
+ SLANG_RETURN_ON_FAIL(addAllDescriptorSets());
- // First, collect `VkDescriptorSetLayout`s for the global scope and all sub-objects
- // referenced via a `ParameterBlock` from shader object layouts.
- SLANG_RETURN_ON_FAIL(addDescriptorSetLayoutRec(this));
+ // We will also use a recursive walk to collect all the push-constant
+ // ranges needed for this object, sub-objects, and entry points.
+ //
+ SLANG_RETURN_ON_FAIL(addAllPushConstantRanges());
- // Next, collect push constant ranges. We will use one descriptor range for each
- // entry point that has uniform parameters.
- uint32_t pushConstantOffset = 0;
- for (auto& entryPoint : m_entryPoints)
- {
- auto size = entryPoint.layout->getPushConstantSize();
- if (size)
- {
- VkPushConstantRange pushConstantRange = {};
- pushConstantRange.offset = pushConstantOffset;
- pushConstantRange.size = size;
- pushConstantRange.stageFlags = entryPoint.layout->getStage();
- m_pushConstantRanges.add(pushConstantRange);
- pushConstantOffset += size;
- }
- }
+ // Once we've collected the information across the entire
+ // tree of sub-objects
// Now call Vulkan API to create a pipeline layout.
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutCreateInfo.setLayoutCount = (uint32_t)m_vkDescriptorSetLayouts.getCount();
pipelineLayoutCreateInfo.pSetLayouts = m_vkDescriptorSetLayouts.getBuffer();
- if (m_pushConstantRanges.getCount())
+ if (m_allPushConstantRanges.getCount())
{
pipelineLayoutCreateInfo.pushConstantRangeCount =
- (uint32_t)m_pushConstantRanges.getCount();
+ (uint32_t)m_allPushConstantRanges.getCount();
pipelineLayoutCreateInfo.pPushConstantRanges =
- m_pushConstantRanges.getBuffer();
+ m_allPushConstantRanges.getBuffer();
}
SLANG_RETURN_ON_FAIL(m_renderer->m_api.vkCreatePipelineLayout(
m_renderer->m_api.m_device, &pipelineLayoutCreateInfo, nullptr, &m_pipelineLayout));
return SLANG_OK;
}
- // Recusively add `VkDescriptorSetLayout` for all descriptor sets used by this and children
- // shader objects and add them to `m_vkDescriptorSetLayouts`.
- Result addDescriptorSetLayoutRec(ShaderObjectLayoutImpl* layout)
+ /// Add all the descriptor sets implied by this root object and sub-objects
+ Result addAllDescriptorSets()
+ {
+ SLANG_RETURN_ON_FAIL(addAllDescriptorSetsRec(this));
+
+ // Note: the descriptor ranges/sets for direct entry point parameters
+ // were already enumerated into the ranges/sets of the root object itself,
+ // so we don't wnat to add them again.
+ //
+ // We do however have to deal with the possibility that an entry
+ // point could introduce "child" descriptor sets, e.g., because it
+ // has a `ParameterBlock<X>` parameter.
+ //
+ for(auto& entryPoint : getEntryPoints())
+ {
+ SLANG_RETURN_ON_FAIL(addChildDescriptorSetsRec(entryPoint.layout));
+ }
+
+ return SLANG_OK;
+ }
+
+ /// Recurisvely add descriptor sets defined by `layout` and sub-objects
+ Result addAllDescriptorSetsRec(ShaderObjectLayoutImpl* layout)
{
- for (auto& descSetInfo : layout->getDescriptorSets())
+ // TODO: This logic assumes that descriptor sets are all contiguous
+ // and have been allocated in a global order that matches the order
+ // of enumeration here.
+
+ for (auto& descSetInfo : layout->getOwnDescriptorSets())
{
m_vkDescriptorSetLayouts.add(descSetInfo.descriptorSetLayout);
}
- // Note: entry point parameters in a `RootShaderObject` has already been included
- // in `layout->getDescriptorSets()` during `RootShaderObjectLayout` construction,
- // so we do not need to enumerate entry point array here.
-
- // However, for sub-objects referenced through `ParameterBlock`s, we do need to
- // add their descriptor sets to our pipeline layout.
- // Binding ranges for sub-objects referenced through `ConstantBuffer`s are also
- // included in this object's layout already, so no need to skip those.
+ SLANG_RETURN_ON_FAIL(addChildDescriptorSetsRec(layout));
+ return SLANG_OK;
+ }
+ /// Recurisvely add descriptor sets defined by sub-objects of `layout`
+ Result addChildDescriptorSetsRec(ShaderObjectLayoutImpl* layout)
+ {
for (auto& subObject : layout->getSubObjectRanges())
{
auto bindingRange = layout->getBindingRange(subObject.bindingRangeIndex);
- if (bindingRange.bindingType == slang::BindingType::ParameterBlock)
+ switch(bindingRange.bindingType)
+ {
+ case slang::BindingType::ParameterBlock:
+ SLANG_RETURN_ON_FAIL(addAllDescriptorSetsRec(subObject.layout));
+ break;
+
+ default:
+ if(auto subObjectLayout = subObject.layout)
+ {
+ SLANG_RETURN_ON_FAIL(addChildDescriptorSetsRec(subObject.layout));
+ }
+ break;
+ }
+ }
+
+ return SLANG_OK;
+ }
+
+ /// Add all the push-constant ranges implied by this root object and sub-objects
+ Result addAllPushConstantRanges()
+ {
+ SLANG_RETURN_ON_FAIL(addAllPushConstantRangesRec(this));
+
+ for(auto& entryPoint : getEntryPoints())
+ {
+ SLANG_RETURN_ON_FAIL(addChildPushConstantRangesRec(entryPoint.layout));
+ }
+
+ return SLANG_OK;
+ }
+
+ /// Recurisvely add push-constant ranges defined by `layout` and sub-objects
+ Result addAllPushConstantRangesRec(ShaderObjectLayoutImpl* layout)
+ {
+ // TODO: This logic assumes that push-constant ranges are all contiguous
+ // and have been allocated in a global order that matches the order
+ // of enumeration here.
+
+ for (auto pushConstantRange : layout->getOwnPushConstantRanges())
+ {
+ pushConstantRange.offset = m_totalPushConstantSize;
+ m_totalPushConstantSize += pushConstantRange.size;
+
+ m_allPushConstantRanges.add(pushConstantRange);
+ }
+
+ SLANG_RETURN_ON_FAIL(addChildPushConstantRangesRec(layout));
+ return SLANG_OK;
+ }
+
+ /// Recurisvely add push-constant ranges defined by sub-objects of `layout`
+ Result addChildPushConstantRangesRec(ShaderObjectLayoutImpl* layout)
+ {
+ for (auto& subObject : layout->getSubObjectRanges())
+ {
+ if(auto subObjectLayout = subObject.layout)
{
- SLANG_RETURN_ON_FAIL(addDescriptorSetLayoutRec(subObject.layout));
+ SLANG_RETURN_ON_FAIL(addAllPushConstantRangesRec(subObject.layout));
}
}
@@ -1308,7 +2019,10 @@ public:
List<EntryPointInfo> m_entryPoints;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
Array<VkDescriptorSetLayout, kMaxDescriptorSets> m_vkDescriptorSetLayouts;
- Array<VkPushConstantRange, 8> m_pushConstantRanges;
+ List<VkPushConstantRange> m_allPushConstantRanges;
+ uint32_t m_totalPushConstantSize = 0;
+
+ SimpleBindingOffset m_pendingDataOffset;
VKDevice* m_renderer = nullptr;
};
@@ -1441,7 +2155,6 @@ public:
void flushBindingState(VkPipelineBindPoint pipelineBindPoint)
{
auto& api = *m_api;
- bindRootShaderObjectImpl(pipelineBindPoint);
// Get specialized pipeline state and bind it.
//
@@ -1449,6 +2162,9 @@ public:
m_device->maybeSpecializePipeline(
m_currentPipeline, &m_commandBuffer->m_rootObject, newPipeline);
PipelineStateImpl* newPipelineImpl = static_cast<PipelineStateImpl*>(newPipeline.Ptr());
+
+ bindRootShaderObjectImpl(pipelineBindPoint);
+
auto pipelineBindPointId = getBindPointIndex(pipelineBindPoint);
if (m_boundPipelines[pipelineBindPointId] != newPipelineImpl->m_pipeline)
{
@@ -1459,28 +2175,23 @@ public:
}
};
- union VulkanDescriptorInfo
- {
- VkDescriptorBufferInfo bufferInfo;
- VkDescriptorImageInfo imageInfo;
- };
- struct RootBindingState
+ /// Context information required when binding shader objects to the pipeline
+ struct RootBindingContext
{
- ShortList<VkWriteDescriptorSet, 32> descriptorSetWrites;
- ChunkedList<VulkanDescriptorInfo, 32> descriptorInfos;
- ChunkedList<VkBufferView, 8> bufferViews;
- Array<VkDescriptorSet, kMaxDescriptorSets> descriptorSets;
- ArrayView<VkPushConstantRange> pushConstantRanges;
+ /// The pipeline layout being used for binding
VkPipelineLayout pipelineLayout;
+
+ /// An allocator to use for descriptor sets during binding
DescriptorSetAllocator* descriptorSetAllocator;
+
+ /// The dvice being used
VKDevice* device;
- };
- struct BindingOffset
- {
- uint32_t uniformOffset;
- uint32_t pushConstantRangeOffset;
- uint32_t descriptorSetIndexOffset;
- uint32_t descriptorRangeOffset;
+
+ /// The descriptor sets that are being allocated and bound
+ VkDescriptorSet* descriptorSets;
+
+ /// Information about all the push-constant ranges that should be bound
+ ConstArrayView<VkPushConstantRange> pushConstantRanges;
};
class ShaderObjectImpl : public ShaderObjectBase
@@ -1949,10 +2660,8 @@ public:
// layout logic does for complex cases with multiple layers of nested arrays and
// structures.
//
- size_t subObjectRangePendingDataOffset =
- _getSubObjectRangePendingDataOffset(specializedLayout, subObjectRangeIndex);
- size_t subObjectRangePendingDataStride =
- _getSubObjectRangePendingDataStride(specializedLayout, subObjectRangeIndex);
+ size_t subObjectRangePendingDataOffset = subObjectRangeInfo.offset.pendingOrdinaryData;
+ size_t subObjectRangePendingDataStride = subObjectRangeInfo.stride.pendingOrdinaryData;
// If the range doesn't actually need/use the "pending" allocation at all, then
// we need to detect that case and skip such ranges.
@@ -1987,22 +2696,6 @@ public:
return SLANG_OK;
}
- // As discussed in `_writeOrdinaryData()`, these methods are just stubs waiting for
- // the "flat" Slang refelction information to provide access to the relevant data.
- //
- size_t _getSubObjectRangePendingDataOffset(
- ShaderObjectLayoutImpl* specializedLayout,
- Index subObjectRangeIndex)
- {
- return 0;
- }
- size_t _getSubObjectRangePendingDataStride(
- ShaderObjectLayoutImpl* specializedLayout,
- Index subObjectRangeIndex)
- {
- return 0;
- }
-
public:
struct CombinedTextureSamplerSlot
{
@@ -2011,218 +2704,219 @@ public:
operator bool() { return textureView && sampler; }
};
- // A shared template function for composing a VkWriteDescriptorSet structure.
- // The signature for `WriteDescriptorInfoFunc` is
- // `void(VkWriteDescriptorSet&, int startElement, int elementCount)`, which sets up
- // `VkWriteDescriptorSet::pBufferInfo`, `pImageInfo` or `pTexelBufferView` fields.
- template<typename WriteDescriptorInfoFunc, typename TResourceArrayView>
- static void _writeDescriptorRange(
- RootBindingState* bindingState,
- BindingOffset offset,
- VkDescriptorType descriptorType,
- TResourceArrayView resourceViews,
- const WriteDescriptorInfoFunc& writeDescriptorInfo)
+ /// Write a single desriptor using the Vulkan API
+ static inline void writeDescriptor(
+ RootBindingContext& context,
+ VkWriteDescriptorSet const& write)
{
- auto descriptorSet = bindingState->descriptorSets[offset.descriptorSetIndexOffset];
- bool hasNullBinding = false;
- for (auto& ptr : resourceViews)
- {
- if (!ptr)
- {
- hasNullBinding = true;
- break;
- }
- }
- if (hasNullBinding)
- {
- for (Index i = 0; i < resourceViews.getCount(); i++)
- {
- if (!resourceViews[i])
- continue;
- VkWriteDescriptorSet write = {};
- write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- write.descriptorCount = 1;
- write.descriptorType = descriptorType;
- write.dstArrayElement = (uint32_t)i;
- write.dstBinding = offset.descriptorRangeOffset;
- write.dstSet = descriptorSet;
- auto infos = bindingState->descriptorInfos.reserveRange(1);
- writeDescriptorInfo(write, (uint32_t)i, 1);
- bindingState->descriptorSetWrites.add(write);
- }
- return;
- }
-
- VkWriteDescriptorSet write = {};
- write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
- write.descriptorCount = (uint32_t)resourceViews.getCount();
- write.descriptorType = descriptorType;
- write.dstArrayElement = 0;
- write.dstBinding = offset.descriptorRangeOffset;
- write.dstSet = descriptorSet;
- writeDescriptorInfo(write, 0, write.descriptorCount);
- bindingState->descriptorSetWrites.add(write);
+ auto device = context.device;
+ device->m_api.vkUpdateDescriptorSets(
+ device->m_device,
+ 1,
+ &write,
+ 0,
+ nullptr);
}
static void writeBufferDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
BufferResourceImpl* buffer,
size_t bufferOffset,
size_t bufferSize)
{
- auto descriptorSet = bindingState->descriptorSets[offset.descriptorSetIndexOffset];
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ VkDescriptorBufferInfo bufferInfo = {};
+ bufferInfo.buffer = buffer->m_buffer.m_buffer;
+ bufferInfo.offset = bufferOffset;
+ bufferInfo.range = bufferSize;
+
VkWriteDescriptorSet write = {};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.descriptorCount = 1;
write.descriptorType = descriptorType;
write.dstArrayElement = 0;
- write.dstBinding = offset.descriptorRangeOffset;
+ write.dstBinding = offset.binding;
write.dstSet = descriptorSet;
- auto& bufferInfo = bindingState->descriptorInfos.reserveRange(1)->bufferInfo;
write.pBufferInfo = &bufferInfo;
- bufferInfo.buffer = buffer->m_buffer.m_buffer;
- bufferInfo.offset = bufferOffset;
- bufferInfo.range = bufferSize;
- bindingState->descriptorSetWrites.add(write);
+
+ writeDescriptor(context, write);
}
static void writeBufferDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
BufferResourceImpl* buffer)
{
writeBufferDescriptor(
- bindingState, offset, descriptorType, buffer, 0, buffer->getDesc()->sizeInBytes);
+ context, offset, descriptorType, buffer, 0, buffer->getDesc()->sizeInBytes);
}
+
static void writePlainBufferDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
ArrayView<RefPtr<ResourceViewImpl>> resourceViews)
{
- auto writeDescriptorInfo = [=](VkWriteDescriptorSet& write,
- uint32_t startElement,
- uint32_t count)
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ Index count = resourceViews.getCount();
+ for(Index i = 0; i < count; ++i)
{
- auto infos = bindingState->descriptorInfos.reserveRange(count);
- write.pBufferInfo = (VkDescriptorBufferInfo*)infos;
- for (uint32_t i = startElement; i < count; i++)
+ auto bufferView = static_cast<PlainBufferResourceViewImpl*>(resourceViews[i].Ptr());
+
+ VkDescriptorBufferInfo bufferInfo = {};
+
+ if(bufferView)
{
- auto bufferView =
- static_cast<PlainBufferResourceViewImpl*>(resourceViews[i].Ptr());
- if (bufferView)
- {
- infos[i].bufferInfo.buffer = bufferView->m_buffer->m_buffer.m_buffer;
- infos[i].bufferInfo.offset = 0;
- infos[i].bufferInfo.range = bufferView->m_buffer->getDesc()->sizeInBytes;
- }
+ bufferInfo.buffer = bufferView->m_buffer->m_buffer.m_buffer;
+ bufferInfo.offset = 0;
+ bufferInfo.range = bufferView->m_buffer->getDesc()->sizeInBytes;
}
- };
- _writeDescriptorRange(
- bindingState, offset, descriptorType, resourceViews, writeDescriptorInfo);
+
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = uint32_t(i);
+ write.dstBinding = offset.binding;
+ write.dstSet = descriptorSet;
+ write.pBufferInfo = &bufferInfo;
+
+ writeDescriptor(context, write);
+ }
}
static void writeTexelBufferDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
ArrayView<RefPtr<ResourceViewImpl>> resourceViews)
{
- auto writeDescriptorInfo = [=](VkWriteDescriptorSet& write,
- uint32_t startElement,
- uint32_t count)
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ Index count = resourceViews.getCount();
+ for(Index i = 0; i < count; ++i)
{
- auto views = bindingState->bufferViews.reserveRange(write.descriptorCount);
- write.pTexelBufferView = views;
- for (uint32_t i = startElement; i < count; i++)
- {
- views[i] =
- static_cast<TexelBufferResourceViewImpl*>(resourceViews[i].Ptr())->m_view;
- }
- };
- _writeDescriptorRange(
- bindingState, offset, descriptorType, resourceViews, writeDescriptorInfo);
+ auto resourceView = static_cast<TexelBufferResourceViewImpl*>(resourceViews[i].Ptr());
+
+ VkBufferView bufferView = resourceView->m_view;
+
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = uint32_t(i);
+ write.dstBinding = offset.binding;
+ write.dstSet = descriptorSet;
+ write.pTexelBufferView = &bufferView;
+
+ writeDescriptor(context, write);
+ }
}
static void writeTextureSamplerDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
ArrayView<CombinedTextureSamplerSlot> slots)
{
- auto writeDescriptorInfo = [=](VkWriteDescriptorSet& write,
- uint32_t startElement,
- uint32_t count)
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ Index count = slots.getCount();
+ for(Index i = 0; i < count; ++i)
{
- auto infos = bindingState->descriptorInfos.reserveRange(write.descriptorCount);
- write.pImageInfo = (VkDescriptorImageInfo*)infos;
- for (uint32_t i = startElement; i < count; i++)
- {
- auto texture = slots[i].textureView;
- auto sampler = slots[i].sampler;
- auto& imageInfo = ((VkDescriptorImageInfo*)infos)[i];
- imageInfo.imageView = texture->m_view;
- imageInfo.imageLayout = texture->m_layout;
- imageInfo.sampler = sampler->m_sampler;
- }
- };
- _writeDescriptorRange(bindingState, offset, descriptorType, slots, writeDescriptorInfo);
+ auto texture = slots[i].textureView;
+ auto sampler = slots[i].sampler;
+
+ VkDescriptorImageInfo imageInfo = {};
+ imageInfo.imageView = texture->m_view;
+ imageInfo.imageLayout = texture->m_layout;
+ imageInfo.sampler = sampler->m_sampler;
+
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = uint32_t(i);
+ write.dstBinding = offset.binding;
+ write.dstSet = descriptorSet;
+ write.pImageInfo = &imageInfo;
+
+ writeDescriptor(context, write);
+ }
}
static void writeTextureDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
ArrayView<RefPtr<ResourceViewImpl>> resourceViews)
{
- auto writeDescriptorInfo =
- [=](VkWriteDescriptorSet& write, uint32_t startElement, uint32_t count)
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ Index count = resourceViews.getCount();
+ for(Index i = 0; i < count; ++i)
{
- auto infos = bindingState->descriptorInfos.reserveRange(write.descriptorCount);
- write.pImageInfo = (VkDescriptorImageInfo*)infos;
- for (uint32_t i = startElement; i < count; i++)
- {
- auto texture = static_cast<TextureResourceViewImpl*>(resourceViews[i].Ptr());
- auto& imageInfo = ((VkDescriptorImageInfo*)infos)[i];
- imageInfo.imageView = texture->m_view;
- imageInfo.imageLayout = texture->m_layout;
- imageInfo.sampler = 0;
- }
- };
- _writeDescriptorRange(
- bindingState, offset, descriptorType, resourceViews, writeDescriptorInfo);
+ auto texture = static_cast<TextureResourceViewImpl*>(resourceViews[i].Ptr());
+
+ VkDescriptorImageInfo imageInfo = {};
+ imageInfo.imageView = texture->m_view;
+ imageInfo.imageLayout = texture->m_layout;
+ imageInfo.sampler = 0;
+
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = uint32_t(i);
+ write.dstBinding = offset.binding;
+ write.dstSet = descriptorSet;
+ write.pImageInfo = &imageInfo;
+
+ writeDescriptor(context, write);
+ }
}
static void writeSamplerDescriptor(
- RootBindingState* bindingState,
- BindingOffset offset,
+ RootBindingContext& context,
+ BindingOffset const& offset,
VkDescriptorType descriptorType,
ArrayView<RefPtr<SamplerStateImpl>> samplers)
{
- auto writeDescriptorInfo =
- [=](VkWriteDescriptorSet& write, uint32_t startElement, uint32_t count)
+ auto descriptorSet = context.descriptorSets[offset.bindingSet];
+
+ Index count = samplers.getCount();
+ for(Index i = 0; i < count; ++i)
{
- auto infos = bindingState->descriptorInfos.reserveRange(write.descriptorCount);
- write.pImageInfo = (VkDescriptorImageInfo*)infos;
- for (uint32_t i = startElement; i < count; i++)
- {
- auto texture = samplers[i]->m_sampler;
- auto& imageInfo = ((VkDescriptorImageInfo*)infos)[i];
- imageInfo.imageView = 0;
- imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
- imageInfo.sampler = samplers[i]->m_sampler;
- }
- };
- _writeDescriptorRange(
- bindingState, offset, descriptorType, samplers, writeDescriptorInfo);
+ auto sampler = samplers[i];
+
+ VkDescriptorImageInfo imageInfo = {};
+ imageInfo.imageView = 0;
+ imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
+ imageInfo.sampler = sampler->m_sampler;
+
+ VkWriteDescriptorSet write = {};
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.descriptorCount = 1;
+ write.descriptorType = descriptorType;
+ write.dstArrayElement = uint32_t(i);
+ write.dstBinding = offset.binding;
+ write.dstSet = descriptorSet;
+ write.pImageInfo = &imageInfo;
+
+ writeDescriptor(context, write);
+ }
}
/// Ensure that the `m_ordinaryDataBuffer` has been created, if it is needed
- Result _ensureOrdinaryDataBufferCreatedIfNeeded(PipelineCommandEncoder* encoder)
+ Result _ensureOrdinaryDataBufferCreatedIfNeeded(
+ PipelineCommandEncoder* encoder,
+ ShaderObjectLayoutImpl* specializedLayout)
{
// If we have already created a buffer to hold ordinary data, then we should
// simply re-use that buffer rather than re-create it.
@@ -2238,22 +2932,7 @@ public:
return SLANG_OK;
}
- // Computing the size of the ordinary data buffer is *not* just as simple
- // as using the size of the `m_ordinayData` array that we store. The reason
- // for the added complexity is that interface-type fields may lead to the
- // storage being specialized such that it needs extra appended data to
- // store the concrete values that logically belong in those interface-type
- // fields but wouldn't fit in the fixed-size allocation we gave them.
- //
- // TODO: We need to actually implement that logic by using reflection
- // data computed for the specialized type of this shader object.
- // For now we just make the simple assumption described above despite
- // knowing that it is false.
- //
- RefPtr<ShaderObjectLayoutImpl> specializedLayout;
- SLANG_RETURN_ON_FAIL(_getSpecializedLayout(specializedLayout.writeRef()));
-
- m_constantBufferSize = specializedLayout->getElementTypeLayout()->getSize();
+ m_constantBufferSize = specializedLayout->getTotalOrdinaryDataSize();
if (m_constantBufferSize == 0)
{
m_upToDateConstantBufferHeapVersion =
@@ -2287,187 +2966,344 @@ public:
return SLANG_OK;
}
- /// Bind the buffer for ordinary/uniform data, if needed
- Result _bindOrdinaryDataBufferIfNeeded(
- PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset)
- {
- // We are going to need to tweak the base binding range index
- // used for descriptor-set writes if and only if we actually
- // bind a buffer for ordinary data.
- //
- auto& baseRangeIndex = offset.descriptorRangeOffset;
-
- // We start by ensuring that the buffer is created, if it is needed.
- //
- SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder));
-
- // If we did indeed need/create a buffer, then we must bind it into
- // the given `descriptorSet` and update the base range index for
- // subsequent binding operations to account for it.
- //
- if (m_constantBuffer)
- {
- auto bufferImpl = static_cast<BufferResourceImpl*>(m_constantBuffer);
- writeBufferDescriptor(
- bindingState,
- offset,
- VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
- bufferImpl,
- m_constantBufferOffset,
- m_constantBufferSize);
- offset.descriptorRangeOffset++;
- }
-
- return SLANG_OK;
- }
-
public:
- Result bindDescriptorRanges(
+
+ /// Bind this shader object as a "value"
+ ///
+ /// This is the mode used for binding sub-objects for existential-type
+ /// fields, and is also used as part of the implementation of the
+ /// parameter-block and constant-buffer cases.
+ ///
+ Result bindAsValue(
PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset)
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout)
{
- auto layout = getLayout();
-
- // Fill in the descriptor sets based on binding ranges
+ // We start by iterating over the "simple" (non-sub-object) binding
+ // ranges and writing them to the descriptor sets that are being
+ // passed down.
//
- for (auto bindingRangeInfo : layout->getBindingRanges())
+ for (auto bindingRangeInfo : specializedLayout->getBindingRanges())
{
- auto rangeIndex =
- bindingRangeInfo.rangeIndexInDescriptorSet + offset.descriptorRangeOffset;
+ BindingOffset rangeOffset = offset;
+
auto baseIndex = bindingRangeInfo.baseIndex;
auto count = (uint32_t)bindingRangeInfo.count;
switch (bindingRangeInfo.bindingType)
{
case slang::BindingType::ConstantBuffer:
- for (uint32_t i = 0; i < count; ++i)
- {
- ShaderObjectImpl* subObject = m_objects[baseIndex + i];
- subObject->bindObjectIntoConstantBuffer(encoder, bindingState, offset);
- }
- break;
case slang::BindingType::ParameterBlock:
- for (uint32_t i = 0; i < count; ++i)
- {
- ShaderObjectImpl* subObject = m_objects[baseIndex + i];
- auto newOffset = offset;
- subObject->bindObjectIntoParameterBlock(encoder, bindingState, newOffset);
- offset.pushConstantRangeOffset = newOffset.pushConstantRangeOffset;
- }
+ case slang::BindingType::ExistentialValue:
break;
+
case slang::BindingType::Texture:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeTextureDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,
m_resourceViews.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::MutableTexture:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeTextureDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
m_resourceViews.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::CombinedTextureSampler:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeTextureSamplerDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
m_combinedTextureSamplers.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::Sampler:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeSamplerDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_SAMPLER,
m_samplers.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::RawBuffer:
case slang::BindingType::MutableRawBuffer:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writePlainBufferDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
m_resourceViews.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::TypedBuffer:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeTexelBufferDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
m_resourceViews.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::MutableTypedBuffer:
+ rangeOffset.bindingSet += bindingRangeInfo.setOffset;
+ rangeOffset.binding += bindingRangeInfo.bindingOffset;
writeTexelBufferDescriptor(
- bindingState,
- offset,
+ context,
+ rangeOffset,
VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,
m_resourceViews.getArrayView(baseIndex, count));
- offset.descriptorRangeOffset++;
break;
case slang::BindingType::VaryingInput:
case slang::BindingType::VaryingOutput:
break;
+ default:
+ SLANG_ASSERT(!"unsupported binding type");
+ return SLANG_FAIL;
+ break;
+ }
+ }
+
+ // Once we've handled the simpel binding ranges, we move on to the
+ // sub-object ranges, which are generally more involved.
+ //
+ for( auto const& subObjectRange : specializedLayout->getSubObjectRanges() )
+ {
+ auto const& bindingRangeInfo = specializedLayout->getBindingRange(subObjectRange.bindingRangeIndex);
+ auto count = bindingRangeInfo.count;
+ auto baseIndex = bindingRangeInfo.baseIndex;
+
+ auto subObjectLayout = subObjectRange.layout;
+
+ // The starting offset to use for the sub-object
+ // has already been computed and stored as part
+ // of the layout, so we can get to the starting
+ // offset for the range easily.
+ //
+ BindingOffset rangeOffset = offset;
+ rangeOffset += subObjectRange.offset;
+
+ switch( bindingRangeInfo.bindingType )
+ {
+ case slang::BindingType::ConstantBuffer:
+ {
+ BindingOffset objOffset = rangeOffset;
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ // Binding a constant buffer sub-object is simple enough:
+ // we just call `bindAsConstantBuffer` on it to bind
+ // the ordinary data buffer (if needed) and any other
+ // bindings it recursively contains.
+ //
+ ShaderObjectImpl* subObject = m_objects[baseIndex + i];
+ subObject->bindAsConstantBuffer(encoder, context, objOffset, subObjectLayout);
+
+ // When dealing with arrays of sub-objects, we need to make
+ // sure to increment the offset for each subsequent object
+ // by the appropriate stride.
+ //
+ // TODO: We should pre-compute these and simply have
+ // `subObjectRange.stride` to go with `subObjectRange.offset`.
+ //
+ objOffset.binding += subObjectLayout->getTotalBindingCount();
+ objOffset.childSet += subObjectLayout->getChildDescriptorSetCount();
+ objOffset.pushConstantRange += subObjectLayout->getTotalPushConstantRangeCount();
+ }
+ }
+ break;
+ case slang::BindingType::ParameterBlock:
+ {
+ BindingOffset objOffset = rangeOffset;
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ // The case for `ParameterBlock<X>` is not that different
+ // from `ConstantBuffer<X>`, except that we call `bindAsParameterBlock`
+ // instead (understandably).
+ //
+ ShaderObjectImpl* subObject = m_objects[baseIndex + i];
+ subObject->bindAsParameterBlock(encoder, context, objOffset, subObjectLayout);
+
+ // The logic for striding from one sub-object to another is also
+ // different, given the differences in how constant buffers and
+ // parameter blocks are allocated.
+ //
+ objOffset.childSet += subObjectLayout->getChildDescriptorSetCount();
+ objOffset.pushConstantRange += subObjectLayout->getTotalPushConstantRangeCount();
+ }
+ }
+ break;
+
case slang::BindingType::ExistentialValue:
+ // Interface/existential-type sub-object ranges are the most complicated case.
//
- // TODO: If the existential value is one that "fits" into the storage available,
- // then we should write its data directly into that area. Otherwise, we need
- // to bind its content as "pending" data which will come after any other data
- // beloning to the same set (that is, it's starting descriptorRangeIndex will
- // need to be one after the number of ranges accounted for in the original type)
+ // First, we can only bind things if we have static specialization information
+ // to work with, which is exactly the case where `subObjectLayout` will be non-null.
//
+ if( subObjectLayout )
+ {
+ // Second, the offset where we want to start bindign for existential-type
+ // ranges is a bit different, because we don't wnat to bind at the "primary"
+ // offset that got passed down, but instead at the "pending" offset.
+ //
+ // For the purposes of nested binding, what used to be the pending offset
+ // will now be used as the primary offset.
+ //
+ BindingOffset objOffset = BindingOffset(rangeOffset.pending);
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ // An existential-type sub-object is always bound just as a value,
+ // which handles its nested bindings and descriptor sets, but
+ // does not deal with ordianry data. The ordinary data should
+ // have been handled as part of the buffer for a parent object
+ // already.
+ //
+ ShaderObjectImpl* subObject = m_objects[baseIndex + i];
+ subObject->bindAsValue(encoder, context, objOffset, subObjectLayout);
+
+ objOffset.binding += subObjectLayout->getTotalBindingCount();
+ objOffset.childSet += subObjectLayout->getChildDescriptorSetCount();
+ objOffset.pushConstantRange += subObjectLayout->getTotalPushConstantRangeCount();
+ }
+ }
break;
default:
- SLANG_ASSERT(!"unsupported binding type");
+ SLANG_ASSERT(!"unsupported sub-object type");
return SLANG_FAIL;
break;
}
}
+
+ return SLANG_OK;
+ }
+
+ /// Allocate the descriptor sets needed for binding this object (but not nested parameter blocks)
+ Result allocateDescriptorSets(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ BindingOffset const& offset,
+ ShaderObjectLayoutImpl* specializedLayout)
+ {
+ auto baseDescriptorSetIndex = offset.childSet;
+
+ // The number of sets to allocate and their layouts was already pre-computed
+ // as part of the shader object layout, so we use that information here.
+ //
+ for (auto descriptorSetInfo : specializedLayout->getOwnDescriptorSets())
+ {
+ auto descriptorSetHandle = context.descriptorSetAllocator->allocate(
+ descriptorSetInfo.descriptorSetLayout).handle;
+
+ // For each set, we need to write it into the set of descriptor sets
+ // being used for binding. This is done both so that other steps
+ // in binding can find the set to fill it in, but also so that
+ // we can bind all the descriptor sets to the pipeline when the
+ // time comes.
+ //
+ auto descriptorSetIndex = baseDescriptorSetIndex + descriptorSetInfo.space;
+ context.descriptorSets[descriptorSetIndex] = descriptorSetHandle;
+ }
+
return SLANG_OK;
}
- virtual Result bindObjectIntoConstantBuffer(
+ /// Bind this object as a `ParameterBlock<X>`.
+ Result bindAsParameterBlock(
PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset)
+ RootBindingContext& context,
+ BindingOffset const& inOffset,
+ ShaderObjectLayoutImpl* specializedLayout)
{
- SLANG_RETURN_ON_FAIL(_bindOrdinaryDataBufferIfNeeded(encoder, bindingState, offset));
+ // Because we are binding into a nested parameter block,
+ // any texture/buffer/sampler bindings will now want to
+ // write into the sets we allocate for this object and
+ // not the sets for any parent object(s).
+ //
+ BindingOffset offset = inOffset;
+ offset.bindingSet = offset.childSet;
+ offset.binding = 0;
+
+ // TODO: We should also be writing to `offset.pending` here,
+ // because any resource/sampler bindings related to "pending"
+ // data should *also* be writing into the chosen set.
+ //
+ // The challenge here is that we need to compute the right
+ // value for `offset.pending.binding`, so that it writes after
+ // all the other bindings.
+
+ // Writing the bindings for a parameter block is relatively easy:
+ // we just need to allocate the descriptor set(s) needed for this
+ // object and then fill it in like a `ConstantBuffer<X>`.
+ //
+ SLANG_RETURN_ON_FAIL(allocateDescriptorSets(encoder, context, offset, specializedLayout));
+ SLANG_RETURN_ON_FAIL(bindAsConstantBuffer(encoder, context, offset, specializedLayout));
- SLANG_RETURN_ON_FAIL(bindDescriptorRanges(encoder, bindingState, offset));
return SLANG_OK;
}
- virtual Result bindObjectIntoParameterBlock(
+ /// Bind the ordinary data buffer if needed, and adjust `ioOffset` accordingly
+ Result bindOrdinaryDataBufferIfNeeded(
PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset)
- {
- auto& descriptorSetInfos = getLayout()->getDescriptorSets();
- offset.descriptorSetIndexOffset = (uint32_t)bindingState->descriptorSets.getCount();
- offset.uniformOffset = 0;
- offset.descriptorRangeOffset = 0;
- for (auto info : descriptorSetInfos)
+ RootBindingContext& context,
+ BindingOffset& ioOffset,
+ ShaderObjectLayoutImpl* specializedLayout)
+ {
+ // We start by ensuring that the buffer is created, if it is needed.
+ //
+ SLANG_RETURN_ON_FAIL(_ensureOrdinaryDataBufferCreatedIfNeeded(encoder, specializedLayout));
+
+ // If we did indeed need/create a buffer, then we must bind it into
+ // the given `descriptorSet` and update the base range index for
+ // subsequent binding operations to account for it.
+ //
+ if (m_constantBuffer)
{
- bindingState->descriptorSets.add(
- bindingState->descriptorSetAllocator->allocate(info.descriptorSetLayout)
- .handle);
+ auto bufferImpl = static_cast<BufferResourceImpl*>(m_constantBuffer);
+ writeBufferDescriptor(
+ context,
+ ioOffset,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+ bufferImpl,
+ m_constantBufferOffset,
+ m_constantBufferSize);
+ ioOffset.binding++;
}
- SLANG_RETURN_ON_FAIL(bindObjectIntoConstantBuffer(encoder, bindingState, offset));
+
+ return SLANG_OK;
+ }
+
+ /// Bind this object as a `ConstantBuffer<X>`.
+ Result bindAsConstantBuffer(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ BindingOffset const& inOffset,
+ ShaderObjectLayoutImpl* specializedLayout)
+ {
+ // To bind an object as a constant buffer, we first
+ // need to bind its ordinary data (if any) into an
+ // ordinary data buffer, and then bind it as a "value"
+ // which handles any of its recursively-contained bindings.
+ //
+ // The one detail is taht when binding the ordinary data
+ // buffer we need to adjust the `binding` index used for
+ // subsequent operations based on whether or not an ordinary
+ // data buffer was used (and thus consumed a `binding`).
+ //
+ BindingOffset offset = inOffset;
+ SLANG_RETURN_ON_FAIL(bindOrdinaryDataBufferIfNeeded(encoder, context, /*inout*/ offset, specializedLayout));
+ SLANG_RETURN_ON_FAIL(bindAsValue(encoder, context, offset, specializedLayout));
return SLANG_OK;
}
@@ -2547,28 +3383,60 @@ public:
EntryPointLayout* getLayout() { return static_cast<EntryPointLayout*>(m_layout.Ptr()); }
- virtual Result bindObjectIntoConstantBuffer(
+ /// Bind this shader object as an entry point
+ Result bindAsEntryPoint(
PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset) override
+ RootBindingContext& context,
+ BindingOffset const& inOffset,
+ EntryPointLayout* layout)
{
- // Set data in `m_ordinaryData` into the push constant range.
+ BindingOffset offset = inOffset;
+
+ // Any ordinary data in an entry point is assumed to be allocated
+ // as a push-constant range.
+ //
+ // TODO: Can we make this operation not bake in that assumption?
+ //
+ // TODO: Can/should this function be renamed as just `bindAsPushConstantBuffer`?
+ //
if (m_ordinaryData.getCount())
{
- auto pushConstantRange =
- bindingState->pushConstantRanges[offset.pushConstantRangeOffset];
+ // The index of the push constant range to bind should be
+ // passed down as part of the `offset`, and we will increment
+ // it here so that any further recursively-contained push-constant
+ // ranges use the next index.
+ //
+ auto pushConstantRangeIndex = offset.pushConstantRange++;
+
+ // Information about the push constant ranges (including offsets
+ // and stage flags) was pre-computed for the entire program and
+ // stored on the binding context.
+ //
+ auto const& pushConstantRange = context.pushConstantRanges[pushConstantRangeIndex];
+
+ // We expect that the size of the range as reflected matches the
+ // amount of ordinary data stored on this object.
+ //
+ // TODO: This would not be the case if specialization for interface-type
+ // parameters led to the entry point having "pending" ordinary data.
+ //
+ SLANG_ASSERT(pushConstantRange.size == (uint32_t) m_ordinaryData.getCount());
+
+ auto pushConstantData = m_ordinaryData.getBuffer();
+
encoder->m_api->vkCmdPushConstants(
encoder->m_commandBuffer->m_commandBuffer,
- bindingState->pipelineLayout,
+ context.pipelineLayout,
pushConstantRange.stageFlags,
pushConstantRange.offset,
pushConstantRange.size,
- m_ordinaryData.getBuffer());
- offset.pushConstantRangeOffset++;
+ pushConstantData);
}
- // Process the rest of binding ranges.
- SLANG_RETURN_ON_FAIL(bindDescriptorRanges(encoder, bindingState, offset));
+ // Any remaining bindings in the object can be handled through the
+ // "value" case.
+ //
+ SLANG_RETURN_ON_FAIL(bindAsValue(encoder, context, offset, layout));
return SLANG_OK;
}
@@ -2614,18 +3482,50 @@ public:
return SLANG_OK;
}
- virtual Result bindObjectIntoParameterBlock(
- PipelineCommandEncoder* encoder,
- RootBindingState* bindingState,
- BindingOffset& offset) override
+ /// Bind this object as a root shader object
+ Result bindAsRoot(
+ PipelineCommandEncoder* encoder,
+ RootBindingContext& context,
+ RootShaderObjectLayout* layout)
{
- SLANG_RETURN_ON_FAIL(Super::bindObjectIntoParameterBlock(encoder, bindingState, offset));
+ BindingOffset offset = {};
+ offset.pending = layout->getPendingDataOffset();
- // Bind all entry points.
- for (auto& entryPoint : m_entryPoints)
+ // Note: the operations here are quite similar to what `bindAsParameterBlock` does.
+ // The key difference in practice is that we do *not* make use of the adjustment
+ // that `bindOrdinaryDataBufferIfNeeded` applied to the offset passed into it.
+ //
+ // The reason for this difference in behavior is that the layout information
+ // for root shader parameters is in practice *already* offset appropriately
+ // (so that it ends up using absolute offsets).
+ //
+ // TODO: One more wrinkle here is that the `ordinaryDataBufferOffset` below
+ // might not be correct if `binding=0,set=0` was already claimed via explicit
+ // binding information. We should really be getting the offset information for
+ // the ordinary data buffer directly from the reflection information for
+ // the global scope.
+
+ SLANG_RETURN_ON_FAIL(allocateDescriptorSets(encoder, context, offset, layout));
+
+ BindingOffset ordinaryDataBufferOffset = offset;
+ SLANG_RETURN_ON_FAIL(bindOrdinaryDataBufferIfNeeded(encoder, context, /*inout*/ ordinaryDataBufferOffset, layout));
+
+ SLANG_RETURN_ON_FAIL(bindAsValue(encoder, context, offset, layout));
+
+ auto entryPointCount = layout->getEntryPoints().getCount();
+ for( Index i = 0; i < entryPointCount; ++i )
{
- entryPoint->bindObjectIntoConstantBuffer(encoder, bindingState, offset);
+ auto entryPoint = m_entryPoints[i];
+ auto const& entryPointInfo = layout->getEntryPoint(i);
+
+ // Note: we do *not* need to add the entry point offset
+ // information to the global `offset` because the
+ // `RootShaderObjectLayout` has already baked any offsets
+ // from the global layout into the `entryPointInfo`.
+
+ entryPoint->bindAsEntryPoint(encoder, context, entryPointInfo.offset, entryPointInfo.layout);
}
+
return SLANG_OK;
}
@@ -3873,33 +4773,50 @@ Result VKDevice::PipelineCommandEncoder::bindRootShaderObjectImpl(
if (!specializedLayout)
return SLANG_FAIL;
- RootBindingState bindState = {};
- bindState.pushConstantRanges = specializedLayout->m_pushConstantRanges.getView();
- bindState.pipelineLayout = specializedLayout->m_pipelineLayout;
- bindState.device = m_device;
- bindState.descriptorSetAllocator = &m_commandBuffer->m_transientHeap->m_descSetAllocator;
-
- // Write bindings into descriptor sets. This step allocate descriptor sets and collects
- // all `VkWriteDescriptorSet` operations in `bindState.descriptorSetWrites`.
- BindingOffset offset = {};
- rootObjectImpl->bindObjectIntoParameterBlock(this, &bindState, offset);
-
- // Execute descriptor writes collected in `bindState.descriptorSetWrites`.
- m_device->m_api.vkUpdateDescriptorSets(
- m_device->m_device,
- (uint32_t)bindState.descriptorSetWrites.getCount(),
- bindState.descriptorSetWrites.getArrayView().arrayView.getBuffer(),
- 0,
- nullptr);
+ // We will set up the context required when binding shader objects
+ // to the pipeline. Note that this is mostly just being packaged
+ // together to minimize the number of parameters that have to
+ // be dealt with in the complex recursive call chains.
+ //
+ RootBindingContext context;
+ context.pipelineLayout = specializedLayout->m_pipelineLayout;
+ context.device = m_device;
+ context.descriptorSetAllocator = &m_commandBuffer->m_transientHeap->m_descSetAllocator;
+ context.pushConstantRanges = specializedLayout->getAllPushConstantRanges().getArrayView();
+
+ // The context includes storage for the descriptor sets we will bind,
+ // and the number of sets we need to make space for is determined
+ // by the specialized program layout.
+ //
+ List<VkDescriptorSet> descriptorSetsStorage;
+ auto descriptorSetCount = specializedLayout->getTotalDescriptorSetCount();
+
+ descriptorSetsStorage.setCount(descriptorSetCount);
+ auto descriptorSets = descriptorSetsStorage.getBuffer();
+
+ context.descriptorSets = descriptorSets;
+
+ // We kick off recursive binding of shader objects to the pipeline (plus
+ // the state in `context`).
+ //
+ // Note: this logic will directly write any push-constant ranges needed,
+ // and will also fill in any descriptor sets. Currently it does not
+ // *bind* the descriptor sets it fills in.
+ //
+ // TODO: It could probably bind the descriptor sets as well.
+ //
+ rootObjectImpl->bindAsRoot(this, context, specializedLayout);
- // Bind descriptor sets.
+ // Once we've filled in all the descriptor sets, we bind them
+ // to the pipeline at once.
+ //
m_device->m_api.vkCmdBindDescriptorSets(
m_commandBuffer->m_commandBuffer,
bindPoint,
specializedLayout->m_pipelineLayout,
0,
- (uint32_t)bindState.descriptorSets.getCount(),
- bindState.descriptorSets.getBuffer(),
+ (uint32_t) descriptorSetCount,
+ descriptorSets,
0,
nullptr);
@@ -5540,7 +6457,7 @@ Result VKDevice::createShaderObjectLayout(
{
RefPtr<ShaderObjectLayoutImpl> layout;
SLANG_RETURN_ON_FAIL(
- ShaderObjectLayoutImpl::createForElementType(this, typeLayout, true, layout.writeRef()));
+ ShaderObjectLayoutImpl::createForElementType(this, typeLayout, layout.writeRef()));
returnRefPtrMove(outLayout, layout);
return SLANG_OK;
}