yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
b118451e3
master
1// vk-shader-object-layout.cpp 2#include "vk-shader-object-layout.h" 3 4namespace gfx 5{ 6 7using namespace Slang ; 8 9namespace vk 10{ 11 12Index ShaderObjectLayoutImpl ::Builder ::findOrAddDescriptorSet (Index space ) 13{ 14Index index ; 15if (m_mapSpaceToDescriptorSetIndex .tryGetValue (space ,index )) 16return index ; 17 18DescriptorSetInfo info = {}; 19info .space = space ; 20 21index = m_descriptorSetBuildInfos .getCount (); 22m_descriptorSetBuildInfos .add (info ); 23 24m_mapSpaceToDescriptorSetIndex .add (space ,index ); 25return index ; 26} 27 28VkDescriptorType ShaderObjectLayoutImpl ::Builder ::_mapDescriptorType ( 29 slang::BindingType slangBindingType ) 30{ 31switch (slangBindingType ) 32 { 33case slang::BindingType ::PushConstant : 34default : 35SLANG_ASSERT ("unsupported binding type" ); 36return VK_DESCRIPTOR_TYPE_MAX_ENUM ; 37 38case slang::BindingType ::Sampler : 39return VK_DESCRIPTOR_TYPE_SAMPLER ; 40case slang::BindingType ::CombinedTextureSampler : 41return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER ; 42case slang::BindingType ::Texture : 43return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE ; 44case slang::BindingType ::MutableTexture : 45return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ; 46case slang::BindingType ::TypedBuffer : 47return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER ; 48case slang::BindingType ::MutableTypedBuffer : 49return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER ; 50case slang::BindingType ::RawBuffer : 51case slang::BindingType ::MutableRawBuffer : 52return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ; 53case slang::BindingType ::InputRenderTarget : 54return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT ; 55case slang::BindingType ::InlineUniformData : 56return VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT ; 57case slang::BindingType ::RayTracingAccelerationStructure : 58return VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR ; 59case slang::BindingType ::ConstantBuffer : 60return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ; 61 } 62} 63 64/// Add any descriptor ranges implied by this object containing a leaf 65/// sub-object described by `typeLayout`, at the given `offset`. 66 67void ShaderObjectLayoutImpl ::Builder ::_addDescriptorRangesAsValue ( 68 slang::TypeLayoutReflection * typeLayout , 69BindingOffset const & offset ) 70{ 71// First we will scan through all the descriptor sets that the Slang reflection 72// information believes go into making up the given type. 73// 74// Note: We are initializing the sets in order so that their order in our 75// internal data structures should be deterministically based on the order 76// in which they are listed in Slang's reflection information. 77// 78Index descriptorSetCount = typeLayout -> getDescriptorSetCount (); 79for (Index i = 0 ;i < descriptorSetCount ;++ i ) 80 { 81SlangInt descriptorRangeCount = typeLayout -> getDescriptorSetDescriptorRangeCount (i ); 82if (descriptorRangeCount == 0 ) 83continue ; 84auto descriptorSetIndex = 85findOrAddDescriptorSet (offset .bindingSet + typeLayout -> getDescriptorSetSpaceOffset (i )); 86 } 87 88// For actually populating the descriptor sets we prefer to enumerate 89// the binding ranges of the type instead of the descriptor sets. 90// 91Index bindRangeCount = typeLayout -> getBindingRangeCount (); 92for (Index i = 0 ;i < bindRangeCount ;++ i ) 93 { 94auto bindingRangeIndex = i ; 95auto bindingRangeType = typeLayout -> getBindingRangeType (bindingRangeIndex ); 96switch (bindingRangeType ) 97 { 98default : 99break ; 100 101// We will skip over ranges that represent sub-objects for now, and handle 102// them in a separate pass. 103// 104case slang::BindingType ::ParameterBlock : 105case slang::BindingType ::ConstantBuffer : 106case slang::BindingType ::ExistentialValue : 107case slang::BindingType ::PushConstant : 108continue ; 109 } 110 111// Given a binding range we are interested in, we will then enumerate 112// its contained descriptor ranges. 113 114Index descriptorRangeCount = 115typeLayout -> getBindingRangeDescriptorRangeCount (bindingRangeIndex ); 116if (descriptorRangeCount == 0 ) 117continue ; 118auto slangDescriptorSetIndex = 119typeLayout -> getBindingRangeDescriptorSetIndex (bindingRangeIndex ); 120auto descriptorSetIndex = findOrAddDescriptorSet ( 121offset .bindingSet + typeLayout -> getDescriptorSetSpaceOffset (slangDescriptorSetIndex )); 122auto & descriptorSetInfo = m_descriptorSetBuildInfos [descriptorSetIndex ]; 123 124Index firstDescriptorRangeIndex = 125typeLayout -> getBindingRangeFirstDescriptorRangeIndex (bindingRangeIndex ); 126for (Index j = 0 ;j < descriptorRangeCount ;++ j ) 127 { 128Index descriptorRangeIndex = firstDescriptorRangeIndex + j ; 129auto slangDescriptorType = typeLayout -> getDescriptorSetDescriptorRangeType ( 130slangDescriptorSetIndex , 131descriptorRangeIndex ); 132 133// Certain kinds of descriptor ranges reflected by Slang do not 134// manifest as descriptors at the Vulkan level, so we will skip those. 135// 136switch (slangDescriptorType ) 137 { 138case slang::BindingType ::ExistentialValue : 139case slang::BindingType ::InlineUniformData : 140case slang::BindingType ::PushConstant : 141continue ; 142default : 143break ; 144 } 145 146auto vkDescriptorType = _mapDescriptorType (slangDescriptorType ); 147VkDescriptorSetLayoutBinding vkBindingRangeDesc = {}; 148vkBindingRangeDesc .binding = 149offset .binding + (uint32_t )typeLayout -> getDescriptorSetDescriptorRangeIndexOffset ( 150slangDescriptorSetIndex , 151descriptorRangeIndex ); 152vkBindingRangeDesc .descriptorCount = 153 (uint32_t )typeLayout -> getDescriptorSetDescriptorRangeDescriptorCount ( 154slangDescriptorSetIndex , 155descriptorRangeIndex ); 156vkBindingRangeDesc .descriptorType = vkDescriptorType ; 157vkBindingRangeDesc .stageFlags = VK_SHADER_STAGE_ALL ; 158 159descriptorSetInfo .vkBindings .add (vkBindingRangeDesc ); 160 } 161 } 162 163// We skipped over the sub-object ranges when adding descriptors above, 164// and now we will address that oversight by iterating over just 165// the sub-object ranges. 166// 167Index subObjectRangeCount = typeLayout -> getSubObjectRangeCount (); 168for (Index subObjectRangeIndex = 0 ;subObjectRangeIndex < subObjectRangeCount ; 169++ subObjectRangeIndex ) 170 { 171auto bindingRangeIndex = 172typeLayout -> getSubObjectRangeBindingRangeIndex (subObjectRangeIndex ); 173auto bindingType = typeLayout -> getBindingRangeType (bindingRangeIndex ); 174 175auto subObjectTypeLayout = typeLayout -> getBindingRangeLeafTypeLayout (bindingRangeIndex ); 176SLANG_ASSERT (subObjectTypeLayout ); 177 178BindingOffset subObjectRangeOffset = offset ; 179subObjectRangeOffset += 180BindingOffset (typeLayout -> getSubObjectRangeOffset (subObjectRangeIndex )); 181 182switch (bindingType ) 183 { 184// A `ParameterBlock<X>` never contributes descripto ranges to the 185// decriptor sets of a parent object. 186// 187case slang::BindingType ::ParameterBlock : 188default : 189break ; 190 191case slang::BindingType ::ExistentialValue : 192// An interest/existential-typed sub-object range will only contribute 193// descriptor ranges to a parent object in the case where it has been 194// specialied, which is precisely the case where the Slang reflection 195// information will tell us about its "pending" layout. 196// 197if (auto pendingTypeLayout = subObjectTypeLayout -> getPendingDataTypeLayout ()) 198 { 199BindingOffset pendingOffset = BindingOffset (subObjectRangeOffset .pending ); 200_addDescriptorRangesAsValue (pendingTypeLayout ,pendingOffset ); 201 } 202break ; 203 204case slang::BindingType ::ConstantBuffer : 205 { 206// A `ConstantBuffer<X>` range will contribute any nested descriptor 207// ranges in `X`, along with a leading descriptor range for a 208// uniform buffer to hold ordinary/uniform data, if there is any. 209 210SLANG_ASSERT (subObjectTypeLayout ); 211 212auto containerVarLayout = subObjectTypeLayout -> getContainerVarLayout (); 213SLANG_ASSERT (containerVarLayout ); 214 215auto elementVarLayout = subObjectTypeLayout -> getElementVarLayout (); 216SLANG_ASSERT (elementVarLayout ); 217 218auto elementTypeLayout = elementVarLayout -> getTypeLayout (); 219SLANG_ASSERT (elementTypeLayout ); 220 221BindingOffset containerOffset = subObjectRangeOffset ; 222containerOffset += BindingOffset (subObjectTypeLayout -> getContainerVarLayout ()); 223 224BindingOffset elementOffset = subObjectRangeOffset ; 225elementOffset += BindingOffset (elementVarLayout ); 226 227_addDescriptorRangesAsConstantBuffer ( 228elementTypeLayout , 229containerOffset , 230elementOffset ); 231 } 232break ; 233 234case slang::BindingType ::PushConstant : 235 { 236// This case indicates a `ConstantBuffer<X>` that was marked as being 237// used for push constants. 238// 239// Much of the handling is the same as for an ordinary 240// `ConstantBuffer<X>`, but of course we need to handle the ordinary 241// data part differently. 242 243SLANG_ASSERT (subObjectTypeLayout ); 244 245auto containerVarLayout = subObjectTypeLayout -> getContainerVarLayout (); 246SLANG_ASSERT (containerVarLayout ); 247 248auto elementVarLayout = subObjectTypeLayout -> getElementVarLayout (); 249SLANG_ASSERT (elementVarLayout ); 250 251auto elementTypeLayout = elementVarLayout -> getTypeLayout (); 252SLANG_ASSERT (elementTypeLayout ); 253 254BindingOffset containerOffset = subObjectRangeOffset ; 255containerOffset += BindingOffset (subObjectTypeLayout -> getContainerVarLayout ()); 256 257BindingOffset elementOffset = subObjectRangeOffset ; 258elementOffset += BindingOffset (elementVarLayout ); 259 260_addDescriptorRangesAsPushConstantBuffer ( 261elementTypeLayout , 262containerOffset , 263elementOffset ); 264 } 265break ; 266 } 267 } 268} 269 270/// Add the descriptor ranges implied by a `ConstantBuffer<X>` where `X` is 271/// described by `elementTypeLayout`. 272/// 273/// The `containerOffset` and `elementOffset` are the binding offsets that 274/// should apply to the buffer itself and the contents of the buffer, respectively. 275/// 276 277void ShaderObjectLayoutImpl ::Builder ::_addDescriptorRangesAsConstantBuffer ( 278 slang::TypeLayoutReflection * elementTypeLayout , 279BindingOffset const & containerOffset , 280BindingOffset const & elementOffset ) 281{ 282// If the type has ordinary uniform data fields, we need to make sure to create 283// a descriptor set with a constant buffer binding in the case that the shader 284// object is bound as a stand alone parameter block. 285if (elementTypeLayout -> getSize (SLANG_PARAMETER_CATEGORY_UNIFORM )!= 0 ) 286 { 287auto descriptorSetIndex = findOrAddDescriptorSet (containerOffset .bindingSet ); 288auto & descriptorSetInfo = m_descriptorSetBuildInfos [descriptorSetIndex ]; 289VkDescriptorSetLayoutBinding vkBindingRangeDesc = {}; 290vkBindingRangeDesc .binding = containerOffset .binding ; 291vkBindingRangeDesc .descriptorCount = 1 ; 292vkBindingRangeDesc .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ; 293vkBindingRangeDesc .stageFlags = VK_SHADER_STAGE_ALL ; 294descriptorSetInfo .vkBindings .add (vkBindingRangeDesc ); 295 } 296 297_addDescriptorRangesAsValue (elementTypeLayout ,elementOffset ); 298} 299 300/// Add the descriptor ranges implied by a `PushConstantBuffer<X>` where `X` is 301/// described by `elementTypeLayout`. 302/// 303/// The `containerOffset` and `elementOffset` are the binding offsets that 304/// should apply to the buffer itself and the contents of the buffer, respectively. 305/// 306 307void ShaderObjectLayoutImpl ::Builder ::_addDescriptorRangesAsPushConstantBuffer ( 308 slang::TypeLayoutReflection * elementTypeLayout , 309BindingOffset const & containerOffset , 310BindingOffset const & elementOffset ) 311{ 312// If the type has ordinary uniform data fields, we need to make sure to create 313// a descriptor set with a constant buffer binding in the case that the shader 314// object is bound as a stand alone parameter block. 315auto ordinaryDataSize = (uint32_t )elementTypeLayout -> getSize (SLANG_PARAMETER_CATEGORY_UNIFORM ); 316if (ordinaryDataSize != 0 ) 317 { 318auto pushConstantRangeIndex = containerOffset .pushConstantRange ; 319 320VkPushConstantRange vkPushConstantRange = {}; 321vkPushConstantRange .size = ordinaryDataSize ; 322vkPushConstantRange .stageFlags = VK_SHADER_STAGE_ALL ;// TODO: be more clever 323 324while ((uint32_t )m_ownPushConstantRanges .getCount () <=pushConstantRangeIndex ) 325 { 326VkPushConstantRange emptyRange = {0 }; 327m_ownPushConstantRanges .add (emptyRange ); 328 } 329 330m_ownPushConstantRanges [pushConstantRangeIndex ]= vkPushConstantRange ; 331 } 332 333_addDescriptorRangesAsValue (elementTypeLayout ,elementOffset ); 334} 335 336/// Add binding ranges to this shader object layout, as implied by the given 337/// `typeLayout` 338 339void ShaderObjectLayoutImpl ::Builder ::addBindingRanges (slang::TypeLayoutReflection * typeLayout ) 340{ 341SlangInt bindingRangeCount = typeLayout -> getBindingRangeCount (); 342for (SlangInt r = 0 ;r < bindingRangeCount ;++ r ) 343 { 344 slang::BindingType slangBindingType = typeLayout -> getBindingRangeType (r ); 345uint32_t count = (uint32_t )typeLayout -> getBindingRangeBindingCount (r ); 346 slang::TypeLayoutReflection * slangLeafTypeLayout = 347typeLayout -> getBindingRangeLeafTypeLayout (r ); 348 349Index baseIndex = 0 ; 350Index subObjectIndex = 0 ; 351switch (slangBindingType ) 352 { 353case slang::BindingType ::ConstantBuffer : 354case slang::BindingType ::ParameterBlock : 355case slang::BindingType ::ExistentialValue : 356baseIndex = m_subObjectCount ; 357subObjectIndex = baseIndex ; 358m_subObjectCount += count ; 359break ; 360case slang::BindingType ::RawBuffer : 361case slang::BindingType ::MutableRawBuffer : 362if (slangLeafTypeLayout -> getType ()-> getElementType ()!= nullptr ) 363 { 364// A structured buffer occupies both a resource slot and 365// a sub-object slot. 366subObjectIndex = m_subObjectCount ; 367m_subObjectCount += count ; 368 } 369baseIndex = m_resourceViewCount ; 370m_resourceViewCount += count ; 371break ; 372case slang::BindingType ::Sampler : 373baseIndex = m_samplerCount ; 374m_samplerCount += count ; 375m_totalBindingCount += 1 ; 376break ; 377 378case slang::BindingType ::CombinedTextureSampler : 379baseIndex = m_combinedTextureSamplerCount ; 380m_combinedTextureSamplerCount += count ; 381m_totalBindingCount += 1 ; 382break ; 383 384case slang::BindingType ::VaryingInput : 385baseIndex = m_varyingInputCount ; 386m_varyingInputCount += count ; 387break ; 388 389case slang::BindingType ::VaryingOutput : 390baseIndex = m_varyingOutputCount ; 391m_varyingOutputCount += count ; 392break ; 393default : 394baseIndex = m_resourceViewCount ; 395m_resourceViewCount += count ; 396m_totalBindingCount += 1 ; 397break ; 398 } 399 400BindingRangeInfo bindingRangeInfo ; 401bindingRangeInfo .bindingType = slangBindingType ; 402bindingRangeInfo .count = count ; 403bindingRangeInfo .baseIndex = baseIndex ; 404bindingRangeInfo .subObjectIndex = subObjectIndex ; 405bindingRangeInfo .isSpecializable = typeLayout -> isBindingRangeSpecializable (r ); 406// We'd like to extract the information on the GLSL/SPIR-V 407// `binding` that this range should bind into (or whatever 408// other specific kind of offset/index is appropriate to it). 409// 410// A binding range represents a logical member of the shader 411// object type, and it may encompass zero or more *descriptor 412// ranges* that describe how it is physically bound to pipeline 413// state. 414// 415// If the current bindign range is backed by at least one descriptor 416// range then we can query the binding offset of that descriptor 417// range. We expect that in the common case there will be exactly 418// one descriptor range, and we can extract the information easily. 419// 420if (typeLayout -> getBindingRangeDescriptorRangeCount (r )!= 0 ) 421 { 422SlangInt descriptorSetIndex = typeLayout -> getBindingRangeDescriptorSetIndex (r ); 423SlangInt descriptorRangeIndex = typeLayout -> getBindingRangeFirstDescriptorRangeIndex (r ); 424 425auto set = typeLayout -> getDescriptorSetSpaceOffset (descriptorSetIndex ); 426auto bindingOffset = typeLayout -> getDescriptorSetDescriptorRangeIndexOffset ( 427descriptorSetIndex , 428descriptorRangeIndex ); 429 430bindingRangeInfo .setOffset = uint32_t (set ); 431bindingRangeInfo .bindingOffset = uint32_t (bindingOffset ); 432 } 433 434m_bindingRanges .add (bindingRangeInfo ); 435 } 436 437SlangInt subObjectRangeCount = typeLayout -> getSubObjectRangeCount (); 438for (SlangInt r = 0 ;r < subObjectRangeCount ;++ r ) 439 { 440SlangInt bindingRangeIndex = typeLayout -> getSubObjectRangeBindingRangeIndex (r ); 441auto & bindingRange = m_bindingRanges [bindingRangeIndex ]; 442auto slangBindingType = typeLayout -> getBindingRangeType (bindingRangeIndex ); 443 slang::TypeLayoutReflection * slangLeafTypeLayout = 444typeLayout -> getBindingRangeLeafTypeLayout (bindingRangeIndex ); 445 446// A sub-object range can either represent a sub-object of a known 447// type, like a `ConstantBuffer<Foo>` or `ParameterBlock<Foo>` 448// (in which case we can pre-compute a layout to use, based on 449// the type `Foo`) *or* it can represent a sub-object of some 450// existential type (e.g., `IBar`) in which case we cannot 451// know the appropraite type/layout of sub-object to allocate. 452// 453RefPtr < ShaderObjectLayoutImpl > subObjectLayout ; 454switch (slangBindingType ) 455 { 456default : 457 { 458auto varLayout = slangLeafTypeLayout -> getElementVarLayout (); 459auto subTypeLayout = varLayout -> getTypeLayout (); 460ShaderObjectLayoutImpl ::createForElementType ( 461m_renderer , 462m_session , 463subTypeLayout , 464subObjectLayout .writeRef ()); 465 } 466break ; 467 468case slang::BindingType ::ExistentialValue : 469if (auto pendingTypeLayout = slangLeafTypeLayout -> getPendingDataTypeLayout ()) 470 { 471ShaderObjectLayoutImpl ::createForElementType ( 472m_renderer , 473m_session , 474pendingTypeLayout , 475subObjectLayout .writeRef ()); 476 } 477break ; 478 } 479 480SubObjectRangeInfo subObjectRange ; 481subObjectRange .bindingRangeIndex = bindingRangeIndex ; 482subObjectRange .layout = subObjectLayout ; 483 484// We will use Slang reflection infromation to extract the offset information 485// for each sub-object range. 486// 487// TODO: We should also be extracting the uniform offset here. 488// 489subObjectRange .offset = SubObjectRangeOffset (typeLayout -> getSubObjectRangeOffset (r )); 490subObjectRange .stride = SubObjectRangeStride (slangLeafTypeLayout ); 491 492switch (slangBindingType ) 493 { 494case slang::BindingType ::ParameterBlock : 495m_childDescriptorSetCount += subObjectLayout -> getTotalDescriptorSetCount (); 496m_childPushConstantRangeCount += subObjectLayout -> getTotalPushConstantRangeCount (); 497break ; 498 499case slang::BindingType ::ConstantBuffer : 500m_childDescriptorSetCount += subObjectLayout -> getChildDescriptorSetCount (); 501m_totalBindingCount += subObjectLayout -> getTotalBindingCount (); 502m_childPushConstantRangeCount += subObjectLayout -> getTotalPushConstantRangeCount (); 503break ; 504 505case slang::BindingType ::ExistentialValue : 506if (subObjectLayout ) 507 { 508m_childDescriptorSetCount += subObjectLayout -> getChildDescriptorSetCount (); 509m_totalBindingCount += subObjectLayout -> getTotalBindingCount (); 510m_childPushConstantRangeCount += subObjectLayout -> getTotalPushConstantRangeCount (); 511 512// An interface-type range that includes ordinary data can 513// increase the size of the ordinary data buffer we need to 514// allocate for the parent object. 515// 516uint32_t ordinaryDataEnd = 517subObjectRange .offset .pendingOrdinaryData + 518 (uint32_t )bindingRange .count * subObjectRange .stride .pendingOrdinaryData ; 519 520if (ordinaryDataEnd > m_totalOrdinaryDataSize ) 521 { 522m_totalOrdinaryDataSize = ordinaryDataEnd ; 523 } 524 } 525break ; 526 527default : 528break ; 529 } 530 531m_subObjectRanges .add (subObjectRange ); 532 } 533} 534 535Result ShaderObjectLayoutImpl ::Builder ::setElementTypeLayout ( 536 slang::TypeLayoutReflection * typeLayout ) 537{ 538typeLayout = _unwrapParameterGroups (typeLayout ,m_containerType ); 539m_elementTypeLayout = typeLayout ; 540 541m_totalOrdinaryDataSize = (uint32_t )typeLayout -> getSize (); 542 543// Next we will compute the binding ranges that are used to store 544// the logical contents of the object in memory. These will relate 545// to the descriptor ranges in the various sets, but not always 546// in a one-to-one fashion. 547 548addBindingRanges (typeLayout ); 549 550// Note: This routine does not take responsibility for 551// adding descriptor ranges at all, because the exact way 552// that descriptor ranges need to be added varies between 553// ordinary shader objects, root shader objects, and entry points. 554 555return SLANG_OK ; 556} 557 558SlangResult ShaderObjectLayoutImpl ::Builder ::build (ShaderObjectLayoutImpl ** outLayout ) 559{ 560auto layout = RefPtr < ShaderObjectLayoutImpl > (new ShaderObjectLayoutImpl ()); 561SLANG_RETURN_ON_FAIL (layout -> _init (this )); 562 563returnRefPtrMove (outLayout ,layout ); 564return SLANG_OK ; 565} 566 567Result ShaderObjectLayoutImpl ::createForElementType ( 568DeviceImpl * renderer , 569 slang::ISession * session , 570 slang::TypeLayoutReflection * elementType , 571ShaderObjectLayoutImpl ** outLayout ) 572{ 573Builder builder (renderer ,session ); 574builder .setElementTypeLayout (elementType ); 575 576// When constructing a shader object layout directly from a reflected 577// type in Slang, we want to compute the descriptor sets and ranges 578// that would be used if this object were bound as a parameter block. 579// 580// It might seem like we need to deal with the other cases for how 581// the shader object might be bound, but the descriptor ranges we 582// compute here will only ever be used in parameter-block case. 583// 584// One important wrinkle is that we know that the parameter block 585// allocated for `elementType` will potentially need a buffer `binding` 586// for any ordinary data it contains. 587 588bool needsOrdinaryDataBuffer = 589builder .m_elementTypeLayout -> getSize (SLANG_PARAMETER_CATEGORY_UNIFORM )!= 0 ; 590uint32_t ordinaryDataBufferCount = needsOrdinaryDataBuffer ?1 :0 ; 591 592// When binding the object, we know that the ordinary data buffer will 593// always use a the first available `binding`, so its offset will be 594// all zeroes. 595// 596BindingOffset containerOffset ; 597 598// In contrast, the `binding`s used by all the other entries in the 599// parameter block will need to be offset by one if there was 600// an ordinary data buffer. 601// 602BindingOffset elementOffset ; 603elementOffset .binding = ordinaryDataBufferCount ; 604 605// Furthermore, any `binding`s that arise due to "pending" data 606// in the type of the object (due to specialization for existential types) 607// will need to come after all the other `binding`s that were 608// part of the "primary" (unspecialized) data. 609// 610uint32_t primaryDescriptorCount = 611ordinaryDataBufferCount + (uint32_t )builder .m_elementTypeLayout -> getSize ( 612SLANG_PARAMETER_CATEGORY_DESCRIPTOR_TABLE_SLOT ); 613elementOffset .pending .binding = primaryDescriptorCount ; 614 615// Once we've computed the offset information, we simply add the 616// descriptor ranges as if things were declared as a `ConstantBuffer<X>`, 617// since that is how things will be laid out inside the parameter block. 618// 619builder ._addDescriptorRangesAsConstantBuffer ( 620builder .m_elementTypeLayout , 621containerOffset , 622elementOffset ); 623return builder .build (outLayout ); 624} 625 626ShaderObjectLayoutImpl ::~ShaderObjectLayoutImpl () 627{ 628for (auto & descSetInfo :m_descriptorSetInfos ) 629 { 630getDevice ()-> m_api .vkDestroyDescriptorSetLayout ( 631getDevice ()-> m_api .m_device , 632descSetInfo .descriptorSetLayout , 633nullptr ); 634 } 635} 636 637Result ShaderObjectLayoutImpl ::_init (Builder const * builder ) 638{ 639auto renderer = builder -> m_renderer ; 640 641initBase (renderer ,builder -> m_session ,builder -> m_elementTypeLayout ); 642 643m_bindingRanges = builder -> m_bindingRanges ; 644 645m_descriptorSetInfos = _Move (builder -> m_descriptorSetBuildInfos ); 646m_ownPushConstantRanges = builder -> m_ownPushConstantRanges ; 647m_resourceViewCount = builder -> m_resourceViewCount ; 648m_samplerCount = builder -> m_samplerCount ; 649m_combinedTextureSamplerCount = builder -> m_combinedTextureSamplerCount ; 650m_childDescriptorSetCount = builder -> m_childDescriptorSetCount ; 651m_totalBindingCount = builder -> m_totalBindingCount ; 652m_subObjectCount = builder -> m_subObjectCount ; 653m_subObjectRanges = builder -> m_subObjectRanges ; 654m_totalOrdinaryDataSize = builder -> m_totalOrdinaryDataSize ; 655 656m_containerType = builder -> m_containerType ; 657 658// Create VkDescriptorSetLayout for all descriptor sets. 659for (auto & descriptorSetInfo :m_descriptorSetInfos ) 660 { 661VkDescriptorSetLayoutCreateInfo createInfo = {}; 662createInfo .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO ; 663createInfo .pBindings = descriptorSetInfo .vkBindings .getBuffer (); 664createInfo .bindingCount = (uint32_t )descriptorSetInfo .vkBindings .getCount (); 665VkDescriptorSetLayout vkDescSetLayout ; 666SLANG_RETURN_ON_FAIL (renderer -> m_api .vkCreateDescriptorSetLayout ( 667renderer -> m_api .m_device , 668& createInfo , 669nullptr , 670& vkDescSetLayout )); 671descriptorSetInfo .descriptorSetLayout = vkDescSetLayout ; 672 } 673return SLANG_OK ; 674} 675 676DeviceImpl * ShaderObjectLayoutImpl ::getDevice () 677{ 678return static_cast < DeviceImpl *> (m_renderer ); 679} 680 681Result EntryPointLayout ::Builder ::build (EntryPointLayout ** outLayout ) 682{ 683RefPtr < EntryPointLayout > layout = new EntryPointLayout (); 684SLANG_RETURN_ON_FAIL (layout -> _init (this )); 685 686returnRefPtrMove (outLayout ,layout ); 687return SLANG_OK ; 688} 689 690void EntryPointLayout ::Builder ::addEntryPointParams (slang::EntryPointLayout * entryPointLayout ) 691{ 692m_slangEntryPointLayout = entryPointLayout ; 693setElementTypeLayout (entryPointLayout -> getTypeLayout ()); 694m_shaderStageFlag = VulkanUtil ::getShaderStage (entryPointLayout -> getStage ()); 695 696// Note: we do not bother adding any descriptor sets/ranges here, 697// because the descriptor ranges of an entry point will simply 698// be allocated as part of the descriptor sets for the root 699// shader object. 700} 701 702Result EntryPointLayout ::_init (Builder const * builder ) 703{ 704auto renderer = builder -> m_renderer ; 705 706SLANG_RETURN_ON_FAIL (Super ::_init (builder )); 707 708m_slangEntryPointLayout = builder -> m_slangEntryPointLayout ; 709m_shaderStageFlag = builder -> m_shaderStageFlag ; 710return SLANG_OK ; 711} 712 713RootShaderObjectLayout ::~RootShaderObjectLayout () 714{ 715if (m_pipelineLayout ) 716 { 717m_renderer -> m_api .vkDestroyPipelineLayout ( 718m_renderer -> m_api .m_device , 719m_pipelineLayout , 720nullptr ); 721 } 722} 723 724Index RootShaderObjectLayout ::findEntryPointIndex (VkShaderStageFlags stage ) 725{ 726auto entryPointCount = m_entryPoints .getCount (); 727for (Index i = 0 ;i < entryPointCount ;++ i ) 728 { 729auto entryPoint = m_entryPoints [i ]; 730if (entryPoint .layout -> getShaderStageFlag ()== stage ) 731return i ; 732 } 733return -1 ; 734} 735 736Result RootShaderObjectLayout ::create ( 737DeviceImpl * renderer , 738 slang::IComponentType * program , 739 slang::ProgramLayout * programLayout , 740RootShaderObjectLayout ** outLayout ) 741{ 742RootShaderObjectLayout ::Builder builder (renderer ,program ,programLayout ); 743builder .addGlobalParams (programLayout -> getGlobalParamsVarLayout ()); 744 745SlangInt entryPointCount = programLayout -> getEntryPointCount (); 746for (SlangInt e = 0 ;e < entryPointCount ;++ e ) 747 { 748auto slangEntryPoint = programLayout -> getEntryPointByIndex (e ); 749 750EntryPointLayout ::Builder entryPointBuilder (renderer ,program -> getSession ()); 751entryPointBuilder .addEntryPointParams (slangEntryPoint ); 752 753RefPtr < EntryPointLayout > entryPointLayout ; 754SLANG_RETURN_ON_FAIL (entryPointBuilder .build (entryPointLayout .writeRef ())); 755 756builder .addEntryPoint (entryPointLayout ); 757 } 758 759SLANG_RETURN_ON_FAIL (builder .build (outLayout )); 760 761return SLANG_OK ; 762} 763 764Result RootShaderObjectLayout ::_init (Builder const * builder ) 765{ 766auto renderer = builder -> m_renderer ; 767 768SLANG_RETURN_ON_FAIL (Super ::_init (builder )); 769 770m_program = builder -> m_program ; 771m_programLayout = builder -> m_programLayout ; 772m_entryPoints = _Move (builder -> m_entryPoints ); 773m_pendingDataOffset = builder -> m_pendingDataOffset ; 774m_renderer = renderer ; 775 776// If the program has unbound specialization parameters, 777// then we will avoid creating a final Vulkan pipeline layout. 778// 779// TODO: We should really create the information necessary 780// for binding as part of a separate object, so that we have 781// a clean seperation between what is needed for writing into 782// a shader object vs. what is needed for binding it to the 783// pipeline. We eventually need to be able to create bindable 784// state objects from unspecialized programs, in order to 785// support dynamic dispatch. 786// 787if (m_program -> getSpecializationParamCount ()!= 0 ) 788return SLANG_OK ; 789 790// Otherwise, we need to create a final (bindable) layout. 791// 792// We will use a recursive walk to collect all the `VkDescriptorSetLayout`s 793// that are required for the global scope, sub-objects, and entry points. 794// 795SLANG_RETURN_ON_FAIL (addAllDescriptorSets ()); 796 797// We will also use a recursive walk to collect all the push-constant 798// ranges needed for this object, sub-objects, and entry points. 799// 800SLANG_RETURN_ON_FAIL (addAllPushConstantRanges ()); 801 802// Once we've collected the information across the entire 803// tree of sub-objects 804 805// Now call Vulkan API to create a pipeline layout. 806VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {}; 807pipelineLayoutCreateInfo .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO ; 808pipelineLayoutCreateInfo .setLayoutCount = (uint32_t )m_vkDescriptorSetLayouts .getCount (); 809pipelineLayoutCreateInfo .pSetLayouts = m_vkDescriptorSetLayouts .getBuffer (); 810if (m_allPushConstantRanges .getCount ()) 811 { 812pipelineLayoutCreateInfo .pushConstantRangeCount = 813 (uint32_t )m_allPushConstantRanges .getCount (); 814pipelineLayoutCreateInfo .pPushConstantRanges = m_allPushConstantRanges .getBuffer (); 815 } 816SLANG_RETURN_ON_FAIL (m_renderer -> m_api .vkCreatePipelineLayout ( 817m_renderer -> m_api .m_device , 818& pipelineLayoutCreateInfo , 819nullptr , 820& m_pipelineLayout )); 821return SLANG_OK ; 822} 823 824/// Add all the descriptor sets implied by this root object and sub-objects 825 826Result RootShaderObjectLayout ::addAllDescriptorSets () 827{ 828SLANG_RETURN_ON_FAIL (addAllDescriptorSetsRec (this )); 829 830// Note: the descriptor ranges/sets for direct entry point parameters 831// were already enumerated into the ranges/sets of the root object itself, 832// so we don't wnat to add them again. 833// 834// We do however have to deal with the possibility that an entry 835// point could introduce "child" descriptor sets, e.g., because it 836// has a `ParameterBlock<X>` parameter. 837// 838for (auto & entryPoint :getEntryPoints ()) 839 { 840SLANG_RETURN_ON_FAIL (addChildDescriptorSetsRec (entryPoint .layout )); 841 } 842 843return SLANG_OK ; 844} 845 846/// Recurisvely add descriptor sets defined by `layout` and sub-objects 847 848Result RootShaderObjectLayout ::addAllDescriptorSetsRec (ShaderObjectLayoutImpl * layout ) 849{ 850// TODO: This logic assumes that descriptor sets are all contiguous 851// and have been allocated in a global order that matches the order 852// of enumeration here. 853 854for (auto & descSetInfo :layout -> getOwnDescriptorSets ()) 855 { 856m_vkDescriptorSetLayouts .add (descSetInfo .descriptorSetLayout ); 857 } 858 859SLANG_RETURN_ON_FAIL (addChildDescriptorSetsRec (layout )); 860return SLANG_OK ; 861} 862 863/// Recurisvely add descriptor sets defined by sub-objects of `layout` 864 865Result RootShaderObjectLayout ::addChildDescriptorSetsRec (ShaderObjectLayoutImpl * layout ) 866{ 867for (auto & subObject :layout -> getSubObjectRanges ()) 868 { 869auto bindingRange = layout -> getBindingRange (subObject .bindingRangeIndex ); 870switch (bindingRange .bindingType ) 871 { 872case slang::BindingType ::ParameterBlock : 873SLANG_RETURN_ON_FAIL (addAllDescriptorSetsRec (subObject .layout )); 874break ; 875 876default : 877if (auto subObjectLayout = subObject .layout ) 878 { 879SLANG_RETURN_ON_FAIL (addChildDescriptorSetsRec (subObject .layout )); 880 } 881break ; 882 } 883 } 884 885return SLANG_OK ; 886} 887 888/// Add all the push-constant ranges implied by this root object and sub-objects 889 890Result RootShaderObjectLayout ::addAllPushConstantRanges () 891{ 892SLANG_RETURN_ON_FAIL (addAllPushConstantRangesRec (this )); 893 894for (auto & entryPoint :getEntryPoints ()) 895 { 896SLANG_RETURN_ON_FAIL (addChildPushConstantRangesRec (entryPoint .layout )); 897 } 898 899return SLANG_OK ; 900} 901 902/// Recurisvely add push-constant ranges defined by `layout` and sub-objects 903 904Result RootShaderObjectLayout ::addAllPushConstantRangesRec (ShaderObjectLayoutImpl * layout ) 905{ 906// TODO: This logic assumes that push-constant ranges are all contiguous 907// and have been allocated in a global order that matches the order 908// of enumeration here. 909 910for (auto pushConstantRange :layout -> getOwnPushConstantRanges ()) 911 { 912pushConstantRange .offset = m_totalPushConstantSize ; 913m_totalPushConstantSize += pushConstantRange .size ; 914 915m_allPushConstantRanges .add (pushConstantRange ); 916 } 917 918SLANG_RETURN_ON_FAIL (addChildPushConstantRangesRec (layout )); 919return SLANG_OK ; 920} 921 922/// Recurisvely add push-constant ranges defined by sub-objects of `layout` 923 924Result RootShaderObjectLayout ::addChildPushConstantRangesRec (ShaderObjectLayoutImpl * layout ) 925{ 926for (auto & subObject :layout -> getSubObjectRanges ()) 927 { 928if (auto subObjectLayout = subObject .layout ) 929 { 930SLANG_RETURN_ON_FAIL (addAllPushConstantRangesRec (subObject .layout )); 931 } 932 } 933 934return SLANG_OK ; 935} 936 937Result RootShaderObjectLayout ::Builder ::build (RootShaderObjectLayout ** outLayout ) 938{ 939RefPtr < RootShaderObjectLayout > layout = new RootShaderObjectLayout (); 940SLANG_RETURN_ON_FAIL (layout -> _init (this )); 941returnRefPtrMove (outLayout ,layout ); 942return SLANG_OK ; 943} 944 945void RootShaderObjectLayout ::Builder ::addGlobalParams ( 946 slang::VariableLayoutReflection * globalsLayout ) 947{ 948setElementTypeLayout (globalsLayout -> getTypeLayout ()); 949 950// We need to populate our descriptor sets/ranges with information 951// from the layout of the global scope. 952// 953// While we expect that the parameter in the global scope start 954// at an offset of zero, it is also worth querying the offset 955// information because it could impact the locations assigned 956// to "pending" data in the case of static specialization. 957// 958BindingOffset offset (globalsLayout ); 959 960// Note: We are adding descriptor ranges here based directly on 961// the type of the global-scope layout. The type layout for the 962// global scope will either be something like a `struct GlobalParams` 963// that contains all the global-scope parameters or a `ConstantBuffer<GlobalParams>` 964// and in either case the `_addDescriptorRangesAsValue` can properly 965// add all the ranges implied. 966// 967// As a result we don't require any special-case logic here to 968// deal with the possibility of a "default" constant buffer allocated 969// for global-scope parameters of uniform/ordinary type. 970// 971_addDescriptorRangesAsValue (globalsLayout -> getTypeLayout (),offset ); 972 973// We want to keep track of the offset that was applied to "pending" 974// data because we will need it again later when it comes time to 975// actually bind things. 976// 977m_pendingDataOffset = offset .pending ; 978} 979 980void RootShaderObjectLayout ::Builder ::addEntryPoint (EntryPointLayout * entryPointLayout ) 981{ 982auto slangEntryPointLayout = entryPointLayout -> getSlangLayout (); 983auto entryPointVarLayout = slangEntryPointLayout -> getVarLayout (); 984 985// The offset information for each entry point needs to 986// be adjusted by any offset for "pending" data that 987// was recorded in the global-scope layout. 988// 989// TODO(tfoley): Double-check that this is correct. 990 991BindingOffset entryPointOffset (entryPointVarLayout ); 992entryPointOffset .pending += m_pendingDataOffset ; 993 994EntryPointInfo info ; 995info .layout = entryPointLayout ; 996info .offset = entryPointOffset ; 997 998// Similar to the case for the global scope, we expect the 999// type layout for the entry point parameters to be either 1000// a `struct EntryPointParams` or a `PushConstantBuffer<EntryPointParams>`. 1001// Rather than deal with the different cases here, we will 1002// trust the `_addDescriptorRangesAsValue` code to handle 1003// either case correctly. 1004// 1005_addDescriptorRangesAsValue (entryPointVarLayout -> getTypeLayout (),entryPointOffset ); 1006 1007m_entryPoints .add (info ); 1008} 1009 1010}// namespace vk 1011}// namespace gfx