yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Ellie Hermaszewskaformatf65d756bf

master
16.1 KiB451 linesraw
1// vk-shader-object-layout.h
2#pragma once
3
4#include "vk-base.h"
5#include "vk-device.h"
6#include "vk-helper-functions.h"
7
8namespace gfx
9{
10
11using namespace Slang;
12
13namespace vk
14{
15
16enum
17{
18    kMaxDescriptorSets = 32,
19};
20
21class ShaderObjectLayoutImpl : public ShaderObjectLayoutBase
22{
23public:
24    // A shader object comprises three main kinds of state:
25    //
26    // * Zero or more bytes of ordinary ("uniform") data
27    // * Zero or more *bindings* for textures, buffers, and samplers
28    // * Zero or more *sub-objects* representing nested parameter blocks, etc.
29    //
30    // A shader object *layout* stores information that can be used to
31    // organize these different kinds of state and optimize access to them.
32    //
33    // For example, both texture/buffer/sampler bindings and sub-objects
34    // are organized into logical *binding ranges* by the Slang reflection
35    // API, and a shader object layout will store information about those
36    // ranges in a form that is usable for the Vulkan API:
37
38    struct BindingRangeInfo
39    {
40        slang::BindingType bindingType;
41        Index count;
42        Index baseIndex;
43
44        /// An index into the sub-object array if this binding range is treated
45        /// as a sub-object.
46        Index subObjectIndex;
47
48        /// The `binding` offset to apply for this range
49        uint32_t bindingOffset;
50
51        /// The `set` offset to apply for this range
52        uint32_t setOffset;
53
54        // Note: The 99% case is that `setOffset` will be zero. For any shader object
55        // that was allocated from an ordinary Slang type (anything other than a root
56        // shader object in fact), all of the bindings will have been allocated into
57        // a single logical descriptor set.
58        //
59        // TODO: Ideally we could refactor so that only the root shader object layout
60        // stores a set offset for its binding ranges, and all other objects skip
61        // storing a field that never actually matters.
62
63        // Is this binding range representing a specialization point, such as
64        // an existential value or a ParameterBlock<IFoo>.
65        bool isSpecializable;
66    };
67
68    // Sometimes we just want to iterate over the ranges that represent
69    // sub-objects while skipping over the others, because sub-object
70    // ranges often require extra handling or more state.
71    //
72    // For that reason we also store pre-computed information about each
73    // sub-object range.
74
75    /// Offset information for a sub-object range
76    struct SubObjectRangeOffset : BindingOffset
77    {
78        SubObjectRangeOffset() {}
79
80        SubObjectRangeOffset(slang::VariableLayoutReflection* varLayout)
81            : BindingOffset(varLayout)
82        {
83            if (auto pendingLayout = varLayout->getPendingDataLayout())
84            {
85                pendingOrdinaryData =
86                    (uint32_t)pendingLayout->getOffset(SLANG_PARAMETER_CATEGORY_UNIFORM);
87            }
88        }
89
90        /// The offset for "pending" ordinary data related to this range
91        uint32_t pendingOrdinaryData = 0;
92    };
93
94    /// Stride information for a sub-object range
95    struct SubObjectRangeStride : BindingOffset
96    {
97        SubObjectRangeStride() {}
98
99        SubObjectRangeStride(slang::TypeLayoutReflection* typeLayout)
100        {
101            if (auto pendingLayout = typeLayout->getPendingDataTypeLayout())
102            {
103                pendingOrdinaryData = (uint32_t)pendingLayout->getStride();
104            }
105        }
106
107        /// The stride for "pending" ordinary data related to this range
108        uint32_t pendingOrdinaryData = 0;
109    };
110
111    /// Information about a logical binding range as reported by Slang reflection
112    struct SubObjectRangeInfo
113    {
114        /// The index of the binding range that corresponds to this sub-object range
115        Index bindingRangeIndex;
116
117        /// The layout expected for objects bound to this range (if known)
118        RefPtr<ShaderObjectLayoutImpl> layout;
119
120        /// The offset to use when binding the first object in this range
121        SubObjectRangeOffset offset;
122
123        /// Stride between consecutive objects in this range
124        SubObjectRangeStride stride;
125    };
126
127    struct DescriptorSetInfo
128    {
129        List<VkDescriptorSetLayoutBinding> vkBindings;
130        Slang::Int space = -1;
131        VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE;
132    };
133
134    struct Builder
135    {
136    public:
137        Builder(DeviceImpl* renderer, slang::ISession* session)
138            : m_renderer(renderer), m_session(session)
139        {
140        }
141
142        DeviceImpl* m_renderer;
143        slang::ISession* m_session;
144        slang::TypeLayoutReflection* m_elementTypeLayout;
145
146        /// The container type of this shader object. When `m_containerType` is
147        /// `StructuredBuffer` or `UnsizedArray`, this shader object represents a collection
148        /// instead of a single object.
149        ShaderObjectContainerType m_containerType = ShaderObjectContainerType::None;
150
151        List<BindingRangeInfo> m_bindingRanges;
152        List<SubObjectRangeInfo> m_subObjectRanges;
153
154        Index m_resourceViewCount = 0;
155        Index m_samplerCount = 0;
156        Index m_combinedTextureSamplerCount = 0;
157        Index m_subObjectCount = 0;
158        Index m_varyingInputCount = 0;
159        Index m_varyingOutputCount = 0;
160        List<DescriptorSetInfo> m_descriptorSetBuildInfos;
161        Dictionary<Index, Index> m_mapSpaceToDescriptorSetIndex;
162
163        /// The number of descriptor sets allocated by child/descendent objects
164        uint32_t m_childDescriptorSetCount = 0;
165
166        /// The total number of `binding`s consumed by this object and its children/descendents
167        uint32_t m_totalBindingCount = 0;
168
169        /// The push-constant ranges that belong to this object itself (if any)
170        List<VkPushConstantRange> m_ownPushConstantRanges;
171
172        /// The number of push-constant ranges owned by child/descendent objects
173        uint32_t m_childPushConstantRangeCount = 0;
174
175        uint32_t m_totalOrdinaryDataSize = 0;
176
177        Index findOrAddDescriptorSet(Index space);
178
179        static VkDescriptorType _mapDescriptorType(slang::BindingType slangBindingType);
180
181        /// Add any descriptor ranges implied by this object containing a leaf
182        /// sub-object described by `typeLayout`, at the given `offset`.
183        void _addDescriptorRangesAsValue(
184            slang::TypeLayoutReflection* typeLayout,
185            BindingOffset const& offset);
186
187        /// Add the descriptor ranges implied by a `ConstantBuffer<X>` where `X` is
188        /// described by `elementTypeLayout`.
189        ///
190        /// The `containerOffset` and `elementOffset` are the binding offsets that
191        /// should apply to the buffer itself and the contents of the buffer, respectively.
192        ///
193        void _addDescriptorRangesAsConstantBuffer(
194            slang::TypeLayoutReflection* elementTypeLayout,
195            BindingOffset const& containerOffset,
196            BindingOffset const& elementOffset);
197
198        /// Add the descriptor ranges implied by a `PushConstantBuffer<X>` where `X` is
199        /// described by `elementTypeLayout`.
200        ///
201        /// The `containerOffset` and `elementOffset` are the binding offsets that
202        /// should apply to the buffer itself and the contents of the buffer, respectively.
203        ///
204        void _addDescriptorRangesAsPushConstantBuffer(
205            slang::TypeLayoutReflection* elementTypeLayout,
206            BindingOffset const& containerOffset,
207            BindingOffset const& elementOffset);
208
209        /// Add binding ranges to this shader object layout, as implied by the given
210        /// `typeLayout`
211        void addBindingRanges(slang::TypeLayoutReflection* typeLayout);
212
213        Result setElementTypeLayout(slang::TypeLayoutReflection* typeLayout);
214
215        SlangResult build(ShaderObjectLayoutImpl** outLayout);
216    };
217
218    static Result createForElementType(
219        DeviceImpl* renderer,
220        slang::ISession* session,
221        slang::TypeLayoutReflection* elementType,
222        ShaderObjectLayoutImpl** outLayout);
223
224    ~ShaderObjectLayoutImpl();
225
226    /// Get the number of descriptor sets that are allocated for this object itself
227    /// (if it needed to be bound as a parameter block).
228    ///
229    uint32_t getOwnDescriptorSetCount() { return uint32_t(m_descriptorSetInfos.getCount()); }
230
231    /// Get information about the descriptor sets that would be allocated to
232    /// represent this object itself as a parameter block.
233    ///
234    List<DescriptorSetInfo> const& getOwnDescriptorSets() { return m_descriptorSetInfos; }
235
236    /// Get the number of descriptor sets that would need to be allocated and bound
237    /// to represent the children of this object if it were bound as a parameter
238    /// block.
239    ///
240    /// To a first approximation, this is the number of (transitive) children
241    /// that are declared as `ParameterBlock<X>`.
242    ///
243    uint32_t getChildDescriptorSetCount() { return m_childDescriptorSetCount; }
244
245    /// Get the total number of descriptor sets that would need to be allocated and bound
246    /// to represent this object and its children (transitively) as a parameter block.
247    ///
248    uint32_t getTotalDescriptorSetCount()
249    {
250        return getOwnDescriptorSetCount() + getChildDescriptorSetCount();
251    }
252
253    /// Get the total number of `binding`s required to represent this type and its
254    /// (transitive) children.
255    ///
256    /// Note that this count does *not* include bindings that would be part of child
257    /// parameter blocks, nor does it include the binding for an ordinary data buffer,
258    /// if one is needed.
259    ///
260    uint32_t getTotalBindingCount() { return m_totalBindingCount; }
261
262    /// Get the list of push constant ranges required to bind the state of this object itself.
263    List<VkPushConstantRange> const& getOwnPushConstantRanges() const
264    {
265        return m_ownPushConstantRanges;
266    }
267
268    /// Get the number of push constant ranges required to bind the state of this object itself.
269    uint32_t getOwnPushConstantRangeCount() { return (uint32_t)m_ownPushConstantRanges.getCount(); }
270
271    /// Get the number of push constant ranges required to bind the state of the (transitive)
272    /// children of this object.
273    uint32_t getChildPushConstantRangeCount() { return m_childPushConstantRangeCount; }
274
275    /// Get the total number of push constant ranges required to bind the state of this object
276    /// and its (transitive) children.
277    uint32_t getTotalPushConstantRangeCount()
278    {
279        return getOwnPushConstantRangeCount() + getChildPushConstantRangeCount();
280    }
281
282    uint32_t getTotalOrdinaryDataSize() const { return m_totalOrdinaryDataSize; }
283
284    List<BindingRangeInfo> const& getBindingRanges() { return m_bindingRanges; }
285
286    Index getBindingRangeCount() { return m_bindingRanges.getCount(); }
287
288    BindingRangeInfo const& getBindingRange(Index index) { return m_bindingRanges[index]; }
289
290    Index getResourceViewCount() { return m_resourceViewCount; }
291    Index getSamplerCount() { return m_samplerCount; }
292    Index getCombinedTextureSamplerCount() { return m_combinedTextureSamplerCount; }
293    Index getSubObjectCount() { return m_subObjectCount; }
294
295    SubObjectRangeInfo const& getSubObjectRange(Index index) { return m_subObjectRanges[index]; }
296    List<SubObjectRangeInfo> const& getSubObjectRanges() { return m_subObjectRanges; }
297
298    DeviceImpl* getDevice();
299
300    slang::TypeReflection* getType() { return m_elementTypeLayout->getType(); }
301
302protected:
303    Result _init(Builder const* builder);
304
305    List<DescriptorSetInfo> m_descriptorSetInfos;
306    List<BindingRangeInfo> m_bindingRanges;
307    Index m_resourceViewCount = 0;
308    Index m_samplerCount = 0;
309    Index m_combinedTextureSamplerCount = 0;
310    Index m_subObjectCount = 0;
311    List<VkPushConstantRange> m_ownPushConstantRanges;
312    uint32_t m_childPushConstantRangeCount = 0;
313
314    uint32_t m_childDescriptorSetCount = 0;
315    uint32_t m_totalBindingCount = 0;
316    uint32_t m_totalOrdinaryDataSize = 0;
317
318    List<SubObjectRangeInfo> m_subObjectRanges;
319};
320
321class EntryPointLayout : public ShaderObjectLayoutImpl
322{
323    typedef ShaderObjectLayoutImpl Super;
324
325public:
326    struct Builder : Super::Builder
327    {
328        Builder(DeviceImpl* device, slang::ISession* session)
329            : Super::Builder(device, session)
330        {
331        }
332
333        Result build(EntryPointLayout** outLayout);
334
335        void addEntryPointParams(slang::EntryPointLayout* entryPointLayout);
336
337        slang::EntryPointLayout* m_slangEntryPointLayout = nullptr;
338
339        VkShaderStageFlags m_shaderStageFlag;
340    };
341
342    Result _init(Builder const* builder);
343
344    VkShaderStageFlags getShaderStageFlag() const { return m_shaderStageFlag; }
345
346    slang::EntryPointLayout* getSlangLayout() const { return m_slangEntryPointLayout; };
347
348    slang::EntryPointLayout* m_slangEntryPointLayout;
349    VkShaderStageFlags m_shaderStageFlag;
350};
351
352class RootShaderObjectLayout : public ShaderObjectLayoutImpl
353{
354    typedef ShaderObjectLayoutImpl Super;
355
356public:
357    ~RootShaderObjectLayout();
358
359    /// Information stored for each entry point of the program
360    struct EntryPointInfo
361    {
362        /// Layout of the entry point
363        RefPtr<EntryPointLayout> layout;
364
365        /// Offset for binding the entry point, relative to the start of the program
366        BindingOffset offset;
367    };
368
369    struct Builder : Super::Builder
370    {
371        Builder(
372            DeviceImpl* renderer,
373            slang::IComponentType* program,
374            slang::ProgramLayout* programLayout)
375            : Super::Builder(renderer, program->getSession())
376            , m_program(program)
377            , m_programLayout(programLayout)
378        {
379        }
380
381        Result build(RootShaderObjectLayout** outLayout);
382
383        void addGlobalParams(slang::VariableLayoutReflection* globalsLayout);
384
385        void addEntryPoint(EntryPointLayout* entryPointLayout);
386
387        slang::IComponentType* m_program;
388        slang::ProgramLayout* m_programLayout;
389        List<EntryPointInfo> m_entryPoints;
390
391        /// Offset to apply to "pending" data from this object, sub-objects, and entry points
392        SimpleBindingOffset m_pendingDataOffset;
393    };
394
395    Index findEntryPointIndex(VkShaderStageFlags stage);
396
397    EntryPointInfo const& getEntryPoint(Index index) { return m_entryPoints[index]; }
398
399    List<EntryPointInfo> const& getEntryPoints() const { return m_entryPoints; }
400
401    static Result create(
402        DeviceImpl* renderer,
403        slang::IComponentType* program,
404        slang::ProgramLayout* programLayout,
405        RootShaderObjectLayout** outLayout);
406
407    SimpleBindingOffset const& getPendingDataOffset() const { return m_pendingDataOffset; }
408
409    slang::IComponentType* getSlangProgram() const { return m_program; }
410    slang::ProgramLayout* getSlangProgramLayout() const { return m_programLayout; }
411
412    /// Get all of the push constant ranges that will be bound for this object and all
413    /// (transitive) sub-objects
414    List<VkPushConstantRange> const& getAllPushConstantRanges() { return m_allPushConstantRanges; }
415
416protected:
417    Result _init(Builder const* builder);
418
419    /// Add all the descriptor sets implied by this root object and sub-objects
420    Result addAllDescriptorSets();
421
422    /// Recurisvely add descriptor sets defined by `layout` and sub-objects
423    Result addAllDescriptorSetsRec(ShaderObjectLayoutImpl* layout);
424
425    /// Recurisvely add descriptor sets defined by sub-objects of `layout`
426    Result addChildDescriptorSetsRec(ShaderObjectLayoutImpl* layout);
427
428    /// Add all the push-constant ranges implied by this root object and sub-objects
429    Result addAllPushConstantRanges();
430
431    /// Recurisvely add push-constant ranges defined by `layout` and sub-objects
432    Result addAllPushConstantRangesRec(ShaderObjectLayoutImpl* layout);
433
434    /// Recurisvely add push-constant ranges defined by sub-objects of `layout`
435    Result addChildPushConstantRangesRec(ShaderObjectLayoutImpl* layout);
436
437public:
438    ComPtr<slang::IComponentType> m_program;
439    slang::ProgramLayout* m_programLayout = nullptr;
440    List<EntryPointInfo> m_entryPoints;
441    VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
442    Array<VkDescriptorSetLayout, kMaxDescriptorSets> m_vkDescriptorSetLayouts;
443    List<VkPushConstantRange> m_allPushConstantRanges;
444    uint32_t m_totalPushConstantSize = 0;
445
446    SimpleBindingOffset m_pendingDataOffset;
447    DeviceImpl* m_renderer = nullptr;
448};
449
450} // namespace vk
451} // namespace gfx