summaryrefslogtreecommitdiff
path: root/tools/gfx/vulkan/vk-shader-object-layout.h
diff options
context:
space:
mode:
authorlucy96chen <47800040+lucy96chen@users.noreply.github.com>2022-05-26 10:54:35 -0700
committerGitHub <noreply@github.com>2022-05-26 10:54:35 -0700
commit43e1b7cdc70b2fcac8a3e8ee72f5bc91726f4ec5 (patch)
tree1e4701b4ab324a199b81e1f6c671f6660f1050c5 /tools/gfx/vulkan/vk-shader-object-layout.h
parent5ff4f42c636a67724523e4fe60697cfac64908cd (diff)
Split render-vk.h/.cpp into a set of smaller files (#2244)
* Some preliminary work on splitting render-vk * render-vk split, tests currently crash on null reference * fixed circular include
Diffstat (limited to 'tools/gfx/vulkan/vk-shader-object-layout.h')
-rw-r--r--tools/gfx/vulkan/vk-shader-object-layout.h441
1 files changed, 441 insertions, 0 deletions
diff --git a/tools/gfx/vulkan/vk-shader-object-layout.h b/tools/gfx/vulkan/vk-shader-object-layout.h
new file mode 100644
index 000000000..cf4035f70
--- /dev/null
+++ b/tools/gfx/vulkan/vk-shader-object-layout.h
@@ -0,0 +1,441 @@
+// vk-shader-object-layout.h
+#pragma once
+
+#include "vk-base.h"
+#include "vk-device.h"
+#include "vk-helper-functions.h"
+
+namespace gfx
+{
+
+using namespace Slang;
+
+namespace vk
+{
+
+enum
+{
+ kMaxDescriptorSets = 8,
+};
+
+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;
+
+ /// An index into the sub-object array if this binding range is treated
+ /// as a sub-object.
+ Index subObjectIndex;
+
+ /// 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 represent
+ // 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 : BindingOffset
+ {
+ SubObjectRangeStride() {}
+
+ SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout)
+ {
+ if (auto pendingLayout = typeLayout->getPendingDataTypeLayout())
+ {
+ pendingOrdinaryData = (uint32_t)pendingLayout->getStride();
+ }
+ }
+
+ /// The stride 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
+ {
+ /// 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
+ {
+ List<VkDescriptorSetLayoutBinding> vkBindings;
+ Slang::Int space = -1;
+ VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
+ };
+
+ struct Builder
+ {
+ public:
+ Builder(DeviceImpl* renderer)
+ : m_renderer(renderer)
+ {}
+
+ DeviceImpl* m_renderer;
+ slang::TypeLayoutReflection* m_elementTypeLayout;
+
+ /// The container type of this shader object. When `m_containerType` is
+ /// `StructuredBuffer` or `UnsizedArray`, this shader object represents a collection
+ /// instead of a single object.
+ ShaderObjectContainerType m_containerType = ShaderObjectContainerType::None;
+
+ List<BindingRangeInfo> m_bindingRanges;
+ List<SubObjectRangeInfo> m_subObjectRanges;
+
+ Index m_resourceViewCount = 0;
+ Index m_samplerCount = 0;
+ Index m_combinedTextureSamplerCount = 0;
+ Index m_subObjectCount = 0;
+ Index m_varyingInputCount = 0;
+ Index m_varyingOutputCount = 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);
+
+ static VkDescriptorType _mapDescriptorType(slang::BindingType slangBindingType);
+
+ /// 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);
+
+ /// 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);
+
+ /// 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);
+
+ /// Add binding ranges to this shader object layout, as implied by the given
+ /// `typeLayout`
+ void addBindingRanges(slang::TypeLayoutReflection* typeLayout);
+
+ Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout);
+
+ SlangResult build(ShaderObjectLayoutImpl** outLayout);
+ };
+
+ static Result createForElementType(
+ DeviceImpl* renderer,
+ slang::TypeLayoutReflection* elementType,
+ ShaderObjectLayoutImpl** outLayout);
+
+ ~ShaderObjectLayoutImpl();
+
+ /// 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; }
+
+ /// 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; }
+
+ Index getBindingRangeCount() { return m_bindingRanges.getCount(); }
+
+ BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; }
+
+ Index getResourceViewCount() { return m_resourceViewCount; }
+ Index getSamplerCount() { return m_samplerCount; }
+ Index getCombinedTextureSamplerCount() { return m_combinedTextureSamplerCount; }
+ Index getSubObjectCount() { return m_subObjectCount; }
+
+ SubObjectRangeInfo const& getSubObjectRange(Index index) { return m_subObjectRanges[index]; }
+ List<SubObjectRangeInfo> const& getSubObjectRanges() { return m_subObjectRanges; }
+
+ DeviceImpl* getDevice();
+
+ slang::TypeReflection* getType() { return m_elementTypeLayout->getType(); }
+
+protected:
+ Result _init(Builder const* builder);
+
+ List<DescriptorSetInfo> m_descriptorSetInfos;
+ List<BindingRangeInfo> m_bindingRanges;
+ Index m_resourceViewCount = 0;
+ Index m_samplerCount = 0;
+ Index m_combinedTextureSamplerCount = 0;
+ Index m_subObjectCount = 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;
+};
+
+class EntryPointLayout : public ShaderObjectLayoutImpl
+{
+ typedef ShaderObjectLayoutImpl Super;
+
+public:
+ struct Builder : Super::Builder
+ {
+ Builder(DeviceImpl* device)
+ : Super::Builder(device)
+ {}
+
+ Result build(EntryPointLayout** outLayout);
+
+ void addEntryPointParams(slang::EntryPointLayout* entryPointLayout);
+
+ slang::EntryPointLayout* m_slangEntryPointLayout = nullptr;
+
+ VkShaderStageFlags m_shaderStageFlag;
+ };
+
+ Result _init(Builder const* builder);
+
+ VkShaderStageFlags getShaderStageFlag() const { return m_shaderStageFlag; }
+
+ slang::EntryPointLayout* getSlangLayout() const { return m_slangEntryPointLayout; };
+
+ slang::EntryPointLayout* m_slangEntryPointLayout;
+ VkShaderStageFlags m_shaderStageFlag;
+};
+
+class RootShaderObjectLayout : public ShaderObjectLayoutImpl
+{
+ typedef ShaderObjectLayoutImpl Super;
+
+public:
+ ~RootShaderObjectLayout();
+
+ /// Information stored for each entry point of the program
+ struct EntryPointInfo
+ {
+ /// Layout of the entry point
+ RefPtr<EntryPointLayout> layout;
+
+ /// Offset for binding the entry point, relative to the start of the program
+ BindingOffset offset;
+ };
+
+ struct Builder : Super::Builder
+ {
+ Builder(
+ DeviceImpl* renderer,
+ slang::IComponentType* program,
+ slang::ProgramLayout* programLayout)
+ : Super::Builder(renderer)
+ , m_program(program)
+ , m_programLayout(programLayout)
+ {}
+
+ Result build(RootShaderObjectLayout** outLayout);
+
+ void addGlobalParams(slang::VariableLayoutReflection* globalsLayout);
+
+ void addEntryPoint(EntryPointLayout* entryPointLayout);
+
+ 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);
+
+ EntryPointInfo const& getEntryPoint(Index index) { return m_entryPoints[index]; }
+
+ List<EntryPointInfo> const& getEntryPoints() const { return m_entryPoints; }
+
+ static Result create(
+ DeviceImpl* renderer,
+ slang::IComponentType* program,
+ slang::ProgramLayout* programLayout,
+ RootShaderObjectLayout** outLayout);
+
+ 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);
+
+ /// Add all the descriptor sets implied by this root object and sub-objects
+ Result addAllDescriptorSets();
+
+ /// Recurisvely add descriptor sets defined by `layout` and sub-objects
+ Result addAllDescriptorSetsRec(ShaderObjectLayoutImpl* layout);
+
+ /// Recurisvely add descriptor sets defined by sub-objects of `layout`
+ Result addChildDescriptorSetsRec(ShaderObjectLayoutImpl* layout);
+
+ /// Add all the push-constant ranges implied by this root object and sub-objects
+ Result addAllPushConstantRanges();
+
+ /// Recurisvely add push-constant ranges defined by `layout` and sub-objects
+ Result addAllPushConstantRangesRec(ShaderObjectLayoutImpl* layout);
+
+ /// Recurisvely add push-constant ranges defined by sub-objects of `layout`
+ Result addChildPushConstantRangesRec(ShaderObjectLayoutImpl* layout);
+
+public:
+ ComPtr<slang::IComponentType> m_program;
+ slang::ProgramLayout* m_programLayout = nullptr;
+ List<EntryPointInfo> m_entryPoints;
+ VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
+ Array<VkDescriptorSetLayout, kMaxDescriptorSets> m_vkDescriptorSetLayouts;
+ List<VkPushConstantRange> m_allPushConstantRanges;
+ uint32_t m_totalPushConstantSize = 0;
+
+ SimpleBindingOffset m_pendingDataOffset;
+ DeviceImpl* m_renderer = nullptr;
+};
+
+} // namespace vk
+} // namespace gfx