yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
f65d756bf
master
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{ 18kMaxDescriptorSets = 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 38struct BindingRangeInfo 39 { 40slang ::BindingType bindingType ; 41Index count ; 42Index baseIndex ; 43 44/// An index into the sub-object array if this binding range is treated 45/// as a sub-object. 46Index subObjectIndex ; 47 48/// The `binding` offset to apply for this range 49uint32_t bindingOffset ; 50 51/// The `set` offset to apply for this range 52uint32_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>. 65bool 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 76struct SubObjectRangeOffset : BindingOffset 77{ 78SubObjectRangeOffset () {} 79 80SubObjectRangeOffset ( slang :: VariableLayoutReflection * varLayout) 81: BindingOffset( varLayout ) 82{ 83if (auto pendingLayout = varLayout -> getPendingDataLayout ()) 84{ 85pendingOrdinaryData = 86( uint32_t )pendingLayout -> getOffset ( SLANG_PARAMETER_CATEGORY_UNIFORM ); 87} 88} 89 90/// The offset for "pending" ordinary data related to this range 91uint32_t pendingOrdinaryData = 0 ; 92}; 93 94/// Stride information for a sub-object range 95struct SubObjectRangeStride : BindingOffset 96{ 97SubObjectRangeStride () {} 98 99SubObjectRangeStride ( slang :: TypeLayoutReflection * typeLayout) 100{ 101if (auto pendingLayout = typeLayout -> getPendingDataTypeLayout ()) 102{ 103pendingOrdinaryData = ( uint32_t )pendingLayout -> getStride (); 104} 105} 106 107/// The stride for "pending" ordinary data related to this range 108uint32_t pendingOrdinaryData = 0 ; 109}; 110 111/// Information about a logical binding range as reported by Slang reflection 112struct SubObjectRangeInfo 113{ 114/// The index of the binding range that corresponds to this sub-object range 115Index bindingRangeIndex ; 116 117/// The layout expected for objects bound to this range (if known) 118RefPtr < ShaderObjectLayoutImpl > layout ; 119 120/// The offset to use when binding the first object in this range 121SubObjectRangeOffset offset ; 122 123/// Stride between consecutive objects in this range 124SubObjectRangeStride stride ; 125}; 126 127struct DescriptorSetInfo 128{ 129List < VkDescriptorSetLayoutBinding > vkBindings ; 130Slang :: Int space = -1 ; 131VkDescriptorSetLayout descriptorSetLayout = VK_NULL_HANDLE ; 132}; 133 134struct Builder 135{ 136public : 137Builder ( DeviceImpl * renderer, slang ::ISession * session) 138: m_renderer (renderer), m_session ( session ) 139{ 140} 141 142DeviceImpl * m_renderer ; 143slang :: ISession * m_session ; 144slang :: 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. 149ShaderObjectContainerType m_containerType = ShaderObjectContainerType ::None; 150 151List < BindingRangeInfo > m_bindingRanges ; 152List < SubObjectRangeInfo > m_subObjectRanges ; 153 154Index m_resourceViewCount = 0 ; 155Index m_samplerCount = 0 ; 156Index m_combinedTextureSamplerCount = 0 ; 157Index m_subObjectCount = 0 ; 158Index m_varyingInputCount = 0 ; 159Index m_varyingOutputCount = 0 ; 160List < DescriptorSetInfo > m_descriptorSetBuildInfos ; 161Dictionary < Index , Index > m_mapSpaceToDescriptorSetIndex ; 162 163/// The number of descriptor sets allocated by child/descendent objects 164uint32_t m_childDescriptorSetCount = 0 ; 165 166/// The total number of `binding`s consumed by this object and its children/descendents 167uint32_t m_totalBindingCount = 0 ; 168 169/// The push-constant ranges that belong to this object itself (if any) 170List < VkPushConstantRange > m_ownPushConstantRanges ; 171 172/// The number of push-constant ranges owned by child/descendent objects 173uint32_t m_childPushConstantRangeCount = 0 ; 174 175uint32_t m_totalOrdinaryDataSize = 0 ; 176 177Index findOrAddDescriptorSet ( Index space); 178 179static 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`. 183void _addDescriptorRangesAsValue ( 184slang ::TypeLayoutReflection * typeLayout, 185BindingOffset 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/// 193void _addDescriptorRangesAsConstantBuffer ( 194slang ::TypeLayoutReflection * elementTypeLayout, 195BindingOffset const & containerOffset , 196BindingOffset 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/// 204void _addDescriptorRangesAsPushConstantBuffer ( 205slang ::TypeLayoutReflection * elementTypeLayout, 206BindingOffset const & containerOffset , 207BindingOffset const & elementOffset); 208 209/// Add binding ranges to this shader object layout, as implied by the given 210/// `typeLayout` 211void addBindingRanges ( slang ::TypeLayoutReflection * typeLayout); 212 213Result setElementTypeLayout ( slang ::TypeLayoutReflection * typeLayout); 214 215SlangResult build ( ShaderObjectLayoutImpl ** outLayout); 216}; 217 218static Result createForElementType ( 219DeviceImpl * renderer, 220slang ::ISession * session, 221slang ::TypeLayoutReflection * elementType, 222ShaderObjectLayoutImpl ** 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/// 229uint32_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/// 234List < 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/// 243uint32_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/// 248uint32_t getTotalDescriptorSetCount () 249{ 250return 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/// 260uint32_t getTotalBindingCount () { return m_totalBindingCount; } 261 262/// Get the list of push constant ranges required to bind the state of this object itself. 263List < VkPushConstantRange > const & getOwnPushConstantRanges () const 264{ 265return m_ownPushConstantRanges; 266} 267 268/// Get the number of push constant ranges required to bind the state of this object itself. 269uint32_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. 273uint32_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. 277uint32_t getTotalPushConstantRangeCount () 278{ 279return getOwnPushConstantRangeCount () + getChildPushConstantRangeCount (); 280} 281 282uint32_t getTotalOrdinaryDataSize () const { return m_totalOrdinaryDataSize; } 283 284List < BindingRangeInfo > const & getBindingRanges () { return m_bindingRanges; } 285 286Index getBindingRangeCount () { return m_bindingRanges. getCount (); } 287 288BindingRangeInfo const & getBindingRange ( Index index) { return m_bindingRanges[index]; } 289 290Index getResourceViewCount () { return m_resourceViewCount; } 291Index getSamplerCount () { return m_samplerCount; } 292Index getCombinedTextureSamplerCount () { return m_combinedTextureSamplerCount; } 293Index getSubObjectCount () { return m_subObjectCount; } 294 295SubObjectRangeInfo const & getSubObjectRange ( Index index) { return m_subObjectRanges[index]; } 296List < SubObjectRangeInfo > const & getSubObjectRanges () { return m_subObjectRanges; } 297 298DeviceImpl * getDevice (); 299 300slang :: TypeReflection * getType () { return m_elementTypeLayout -> getType (); } 301 302protected : 303Result _init ( Builder const * builder); 304 305List < DescriptorSetInfo > m_descriptorSetInfos; 306List < BindingRangeInfo > m_bindingRanges; 307Index m_resourceViewCount = 0 ; 308Index m_samplerCount = 0 ; 309Index m_combinedTextureSamplerCount = 0 ; 310Index m_subObjectCount = 0 ; 311List < VkPushConstantRange > m_ownPushConstantRanges; 312uint32_t m_childPushConstantRangeCount = 0 ; 313 314uint32_t m_childDescriptorSetCount = 0 ; 315uint32_t m_totalBindingCount = 0 ; 316uint32_t m_totalOrdinaryDataSize = 0 ; 317 318List < SubObjectRangeInfo > m_subObjectRanges; 319}; 320 321class EntryPointLayout : public ShaderObjectLayoutImpl 322{ 323typedef ShaderObjectLayoutImpl Super ; 324 325public : 326struct Builder : Super ::Builder 327{ 328Builder (DeviceImpl * device, slang::ISession * session) 329: Super :: Builder (device, session) 330{ 331} 332 333Result build ( EntryPointLayout ** outLayout); 334 335void addEntryPointParams ( slang ::EntryPointLayout * entryPointLayout); 336 337slang :: EntryPointLayout * m_slangEntryPointLayout = nullptr ; 338 339VkShaderStageFlags m_shaderStageFlag; 340}; 341 342Result _init ( Builder const * builder); 343 344VkShaderStageFlags getShaderStageFlag () const { return m_shaderStageFlag; } 345 346slang :: EntryPointLayout * getSlangLayout () const { return m_slangEntryPointLayout; }; 347 348slang :: EntryPointLayout * m_slangEntryPointLayout; 349VkShaderStageFlags m_shaderStageFlag; 350}; 351 352class RootShaderObjectLayout : public ShaderObjectLayoutImpl 353{ 354typedef ShaderObjectLayoutImpl Super ; 355 356public : 357~ RootShaderObjectLayout (); 358 359/// Information stored for each entry point of the program 360struct EntryPointInfo 361{ 362/// Layout of the entry point 363RefPtr < EntryPointLayout > layout ; 364 365/// Offset for binding the entry point, relative to the start of the program 366BindingOffset offset ; 367}; 368 369struct Builder : Super::Builder 370{ 371Builder ( 372DeviceImpl * renderer, 373slang::IComponentType * program, 374slang::ProgramLayout * programLayout) 375: Super:: Builder (renderer, program -> getSession ()) 376, m_program (program) 377, m_programLayout (programLayout) 378{ 379} 380 381Result build ( RootShaderObjectLayout ** outLayout); 382 383void addGlobalParams ( slang ::VariableLayoutReflection * globalsLayout); 384 385void addEntryPoint ( EntryPointLayout * entryPointLayout); 386 387slang :: IComponentType * m_program; 388slang :: ProgramLayout * m_programLayout; 389List < EntryPointInfo > m_entryPoints; 390 391/// Offset to apply to "pending" data from this object, sub-objects, and entry points 392SimpleBindingOffset m_pendingDataOffset; 393}; 394 395Index findEntryPointIndex ( VkShaderStageFlags stage); 396 397EntryPointInfo const & getEntryPoint ( Index index) { return m_entryPoints[index]; } 398 399List < EntryPointInfo > const & getEntryPoints () const { return m_entryPoints; } 400 401static Result create ( 402DeviceImpl * renderer, 403slang ::IComponentType * program, 404slang ::ProgramLayout * programLayout, 405RootShaderObjectLayout ** outLayout); 406 407SimpleBindingOffset const & getPendingDataOffset () const { return m_pendingDataOffset; } 408 409slang :: IComponentType * getSlangProgram () const { return m_program; } 410slang :: 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 414List < VkPushConstantRange > const & getAllPushConstantRanges () { return m_allPushConstantRanges; } 415 416protected : 417Result _init ( Builder const * builder); 418 419/// Add all the descriptor sets implied by this root object and sub-objects 420Result addAllDescriptorSets (); 421 422/// Recurisvely add descriptor sets defined by `layout` and sub-objects 423Result addAllDescriptorSetsRec ( ShaderObjectLayoutImpl * layout); 424 425/// Recurisvely add descriptor sets defined by sub-objects of `layout` 426Result addChildDescriptorSetsRec ( ShaderObjectLayoutImpl * layout); 427 428/// Add all the push-constant ranges implied by this root object and sub-objects 429Result addAllPushConstantRanges (); 430 431/// Recurisvely add push-constant ranges defined by `layout` and sub-objects 432Result addAllPushConstantRangesRec ( ShaderObjectLayoutImpl * layout); 433 434/// Recurisvely add push-constant ranges defined by sub-objects of `layout` 435Result addChildPushConstantRangesRec ( ShaderObjectLayoutImpl * layout); 436 437public : 438ComPtr < slang::IComponentType > m_program; 439slang :: ProgramLayout * m_programLayout = nullptr ; 440List < EntryPointInfo > m_entryPoints; 441VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE ; 442Array < VkDescriptorSetLayout, kMaxDescriptorSets > m_vkDescriptorSetLayouts; 443List < VkPushConstantRange > m_allPushConstantRanges; 444uint32_t m_totalPushConstantSize = 0 ; 445 446SimpleBindingOffset m_pendingDataOffset; 447DeviceImpl * m_renderer = nullptr ; 448}; 449 450} // namespace vk 451} // namespace gfx