yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
1681bc67f
master
1// vk-shader-object.cpp 2#include "vk-shader-object.h" 3 4#include "vk-command-buffer.h" 5#include "vk-command-encoder.h" 6#include "vk-transient-heap.h" 7 8namespace gfx 9{ 10 11using namespace Slang ; 12 13namespace vk 14{ 15 16Result ShaderObjectImpl ::create ( 17IDevice * device , 18ShaderObjectLayoutImpl * layout , 19ShaderObjectImpl ** outShaderObject ) 20{ 21auto object = RefPtr < ShaderObjectImpl > (new ShaderObjectImpl ()); 22SLANG_RETURN_ON_FAIL (object -> init (device ,layout )); 23 24returnRefPtrMove (outShaderObject ,object ); 25return SLANG_OK ; 26} 27 28RendererBase * ShaderObjectImpl ::getDevice () 29{ 30return m_layout -> getDevice (); 31} 32 33GfxCount ShaderObjectImpl ::getEntryPointCount () 34{ 35return 0 ; 36} 37 38Result ShaderObjectImpl ::getEntryPoint (GfxIndex index ,IShaderObject ** outEntryPoint ) 39{ 40* outEntryPoint = nullptr ; 41return SLANG_OK ; 42} 43 44const void * ShaderObjectImpl ::getRawData () 45{ 46return m_data .getBuffer (); 47} 48 49Size ShaderObjectImpl ::getSize () 50{ 51return (Size )m_data .getCount (); 52} 53 54// TODO: Change size_t and Index to Size? 55Result ShaderObjectImpl ::setData (ShaderOffset const & inOffset ,void const * data ,size_t inSize ) 56{ 57Index offset = inOffset .uniformOffset ; 58Index size = inSize ; 59 60char * dest = m_data .getBuffer (); 61Index availableSize = m_data .getCount (); 62 63// TODO: We really should bounds-check access rather than silently ignoring sets 64// that are too large, but we have several test cases that set more data than 65// an object actually stores on several targets... 66// 67if (offset < 0 ) 68 { 69size += offset ; 70offset = 0 ; 71 } 72if ((offset + size ) >=availableSize ) 73 { 74size = availableSize - offset ; 75 } 76 77memcpy (dest + offset ,data ,size ); 78 79m_isConstantBufferDirty = true; 80 81return SLANG_OK ; 82} 83 84Result ShaderObjectImpl ::setResource (ShaderOffset const & offset ,IResourceView * resourceView ) 85{ 86if (offset .bindingRangeIndex < 0 ) 87return SLANG_E_INVALID_ARG ; 88auto layout = getLayout (); 89if (offset .bindingRangeIndex >=layout -> getBindingRangeCount ()) 90return SLANG_E_INVALID_ARG ; 91auto & bindingRange = layout -> getBindingRange (offset .bindingRangeIndex ); 92if (!resourceView ) 93 { 94m_resourceViews [bindingRange .baseIndex + offset .bindingArrayIndex ]= nullptr ; 95 } 96else 97 { 98if (resourceView -> getViewDesc ()-> type == IResourceView ::Type ::AccelerationStructure ) 99 { 100m_resourceViews [bindingRange .baseIndex + offset .bindingArrayIndex ]= 101static_cast < AccelerationStructureImpl *> (resourceView ); 102 } 103else 104 { 105m_resourceViews [bindingRange .baseIndex + offset .bindingArrayIndex ]= 106static_cast < ResourceViewImpl *> (resourceView ); 107 } 108 } 109return SLANG_OK ; 110} 111 112Result ShaderObjectImpl ::setSampler (ShaderOffset const & offset ,ISamplerState * sampler ) 113{ 114if (offset .bindingRangeIndex < 0 ) 115return SLANG_E_INVALID_ARG ; 116auto layout = getLayout (); 117if (offset .bindingRangeIndex >=layout -> getBindingRangeCount ()) 118return SLANG_E_INVALID_ARG ; 119auto & bindingRange = layout -> getBindingRange (offset .bindingRangeIndex ); 120 121m_samplers [bindingRange .baseIndex + offset .bindingArrayIndex ]= 122static_cast < SamplerStateImpl *> (sampler ); 123return SLANG_OK ; 124} 125 126Result ShaderObjectImpl ::setCombinedTextureSampler ( 127ShaderOffset const & offset , 128IResourceView * textureView , 129ISamplerState * sampler ) 130{ 131if (offset .bindingRangeIndex < 0 ) 132return SLANG_E_INVALID_ARG ; 133auto layout = getLayout (); 134if (offset .bindingRangeIndex >=layout -> getBindingRangeCount ()) 135return SLANG_E_INVALID_ARG ; 136auto & bindingRange = layout -> getBindingRange (offset .bindingRangeIndex ); 137 138auto & slot = m_combinedTextureSamplers [bindingRange .baseIndex + offset .bindingArrayIndex ]; 139slot .textureView = static_cast < TextureResourceViewImpl *> (textureView ); 140slot .sampler = static_cast < SamplerStateImpl *> (sampler ); 141return SLANG_OK ; 142} 143 144Result ShaderObjectImpl ::init (IDevice * device ,ShaderObjectLayoutImpl * layout ) 145{ 146m_layout = layout ; 147 148m_constantBufferTransientHeap = nullptr ; 149m_constantBufferTransientHeapVersion = 0 ; 150m_isConstantBufferDirty = true; 151 152// If the layout tells us that there is any uniform data, 153// then we will allocate a CPU memory buffer to hold that data 154// while it is being set from the host. 155// 156// Once the user is done setting the parameters/fields of this 157// shader object, we will produce a GPU-memory version of the 158// uniform data (which includes values from this object and 159// any existential-type sub-objects). 160// 161// TODO: Change size_t to Count? 162size_t uniformSize = layout -> getElementTypeLayout ()-> getSize (); 163if (uniformSize ) 164 { 165m_data .setCount (uniformSize ); 166memset (m_data .getBuffer (),0 ,uniformSize ); 167 } 168 169#if 0 170// If the layout tells us there are any descriptor sets to 171// allocate, then we do so now. 172// 173for (auto descriptorSetInfo :layout -> getDescriptorSets ()) 174 { 175RefPtr < DescriptorSet > descriptorSet ; 176SLANG_RETURN_ON_FAIL (renderer -> createDescriptorSet (descriptorSetInfo -> layout ,descriptorSet .writeRef ())); 177m_descriptorSets .add (descriptorSet ); 178 } 179#endif 180 181m_resourceViews .setCount (layout -> getResourceViewCount ()); 182m_samplers .setCount (layout -> getSamplerCount ()); 183m_combinedTextureSamplers .setCount (layout -> getCombinedTextureSamplerCount ()); 184 185// If the layout specifies that we have any sub-objects, then 186// we need to size the array to account for them. 187// 188Index subObjectCount = layout -> getSubObjectCount (); 189m_objects .setCount (subObjectCount ); 190 191for (auto subObjectRangeInfo :layout -> getSubObjectRanges ()) 192 { 193auto subObjectLayout = subObjectRangeInfo .layout ; 194 195// In the case where the sub-object range represents an 196// existential-type leaf field (e.g., an `IBar`), we 197// cannot pre-allocate the object(s) to go into that 198// range, since we can't possibly know what to allocate 199// at this point. 200// 201if (!subObjectLayout ) 202continue ; 203// 204// Otherwise, we will allocate a sub-object to fill 205// in each entry in this range, based on the layout 206// information we already have. 207 208auto & bindingRangeInfo = layout -> getBindingRange (subObjectRangeInfo .bindingRangeIndex ); 209for (Index i = 0 ;i < bindingRangeInfo .count ;++ i ) 210 { 211RefPtr < ShaderObjectImpl > subObject ; 212SLANG_RETURN_ON_FAIL ( 213ShaderObjectImpl ::create (device ,subObjectLayout ,subObject .writeRef ())); 214m_objects [bindingRangeInfo .subObjectIndex + i ]= subObject ; 215 } 216 } 217 218return SLANG_OK ; 219} 220 221Result ShaderObjectImpl ::_writeOrdinaryData ( 222PipelineCommandEncoder * encoder , 223IBufferResource * buffer , 224Offset offset , 225Size destSize , 226ShaderObjectLayoutImpl * specializedLayout ) 227{ 228auto src = m_data .getBuffer (); 229// TODO: Change size_t to Count? 230auto srcSize = size_t (m_data .getCount ()); 231 232SLANG_ASSERT (srcSize <=destSize ); 233 234encoder -> uploadBufferDataImpl (buffer ,offset ,srcSize ,src ); 235 236// In the case where this object has any sub-objects of 237// existential/interface type, we need to recurse on those objects 238// that need to write their state into an appropriate "pending" allocation. 239// 240// Note: Any values that could fit into the "payload" included 241// in the existential-type field itself will have already been 242// written as part of `setObject()`. This loop only needs to handle 243// those sub-objects that do not "fit." 244// 245// An implementers looking at this code might wonder if things could be changed 246// so that *all* writes related to sub-objects for interface-type fields could 247// be handled in this one location, rather than having some in `setObject()` and 248// others handled here. 249// 250Index subObjectRangeCounter = 0 ; 251for (auto const & subObjectRangeInfo :specializedLayout -> getSubObjectRanges ()) 252 { 253Index subObjectRangeIndex = subObjectRangeCounter ++ ; 254auto const & bindingRangeInfo = 255specializedLayout -> getBindingRange (subObjectRangeInfo .bindingRangeIndex ); 256 257// We only need to handle sub-object ranges for interface/existential-type fields, 258// because fields of constant-buffer or parameter-block type are responsible for 259// the ordinary/uniform data of their own existential/interface-type sub-objects. 260// 261if (bindingRangeInfo .bindingType != slang::BindingType ::ExistentialValue ) 262continue ; 263 264// Each sub-object range represents a single "leaf" field, but might be nested 265// under zero or more outer arrays, such that the number of existential values 266// in the same range can be one or more. 267// 268auto count = bindingRangeInfo .count ; 269 270// We are not concerned with the case where the existential value(s) in the range 271// git into the payload part of the leaf field. 272// 273// In the case where the value didn't fit, the Slang layout strategy would have 274// considered the requirements of the value as a "pending" allocation, and would 275// allocate storage for the ordinary/uniform part of that pending allocation inside 276// of the parent object's type layout. 277// 278// Here we assume that the Slang reflection API can provide us with a single byte 279// offset and stride for the location of the pending data allocation in the 280// specialized type layout, which will store the values for this sub-object range. 281// 282// TODO: The reflection API functions we are assuming here haven't been implemented 283// yet, so the functions being called here are stubs. 284// 285// TODO: It might not be that a single sub-object range can reliably map to a single 286// contiguous array with a single stride; we need to carefully consider what the 287// layout logic does for complex cases with multiple layers of nested arrays and 288// structures. 289// 290Offset subObjectRangePendingDataOffset = subObjectRangeInfo .offset .pendingOrdinaryData ; 291Size subObjectRangePendingDataStride = subObjectRangeInfo .stride .pendingOrdinaryData ; 292 293// If the range doesn't actually need/use the "pending" allocation at all, then 294// we need to detect that case and skip such ranges. 295// 296// TODO: This should probably be handled on a per-object basis by caching a "does it 297// fit?" bit as part of the information for bound sub-objects, given that we already 298// compute the "does it fit?" status as part of `setObject()`. 299// 300if (subObjectRangePendingDataOffset == 0 ) 301continue ; 302 303for (Slang ::Index i = 0 ;i < count ;++ i ) 304 { 305auto subObject = m_objects [bindingRangeInfo .subObjectIndex + i ]; 306 307RefPtr < ShaderObjectLayoutImpl > subObjectLayout ; 308SLANG_RETURN_ON_FAIL (subObject -> _getSpecializedLayout (subObjectLayout .writeRef ())); 309 310auto subObjectOffset = 311subObjectRangePendingDataOffset + i * subObjectRangePendingDataStride ; 312 313subObject -> _writeOrdinaryData ( 314encoder , 315buffer , 316offset + subObjectOffset , 317destSize - subObjectOffset , 318subObjectLayout ); 319 } 320 } 321 322return SLANG_OK ; 323} 324 325void ShaderObjectImpl ::writeDescriptor ( 326RootBindingContext & context , 327VkWriteDescriptorSet const & write ) 328{ 329auto device = context .device ; 330device -> m_api .vkUpdateDescriptorSets (device -> m_device ,1 ,& write ,0 ,nullptr ); 331} 332 333void ShaderObjectImpl ::writeBufferDescriptor ( 334RootBindingContext & context , 335BindingOffset const & offset , 336VkDescriptorType descriptorType , 337BufferResourceImpl * buffer , 338Offset bufferOffset , 339Size bufferSize ) 340{ 341auto descriptorSet = (* context .descriptorSets )[offset .bindingSet ]; 342 343VkDescriptorBufferInfo bufferInfo = {}; 344if (buffer ) 345 { 346bufferInfo .buffer = buffer -> m_buffer .m_buffer ; 347 } 348bufferInfo .offset = bufferOffset ; 349bufferInfo .range = bufferSize ; 350 351VkWriteDescriptorSet write = {}; 352write .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ; 353write .descriptorCount = 1 ; 354write .descriptorType = descriptorType ; 355write .dstArrayElement = 0 ; 356write .dstBinding = offset .binding ; 357write .dstSet = descriptorSet ; 358write .pBufferInfo = & bufferInfo ; 359 360writeDescriptor (context ,write ); 361} 362 363void ShaderObjectImpl ::writeBufferDescriptor ( 364RootBindingContext & context , 365BindingOffset const & offset , 366VkDescriptorType descriptorType , 367BufferResourceImpl * buffer ) 368{ 369writeBufferDescriptor ( 370context , 371offset , 372descriptorType , 373buffer , 3740 , 375buffer -> getDesc ()-> sizeInBytes ); 376} 377 378void ShaderObjectImpl ::writePlainBufferDescriptor ( 379RootBindingContext & context , 380BindingOffset const & offset , 381VkDescriptorType descriptorType , 382ArrayView < RefPtr < ResourceViewInternalBase >> resourceViews ) 383{ 384auto descriptorSet = (* context .descriptorSets )[offset .bindingSet ]; 385 386Index count = resourceViews .getCount (); 387for (Index i = 0 ;i < count ;++ i ) 388 { 389VkDescriptorBufferInfo bufferInfo = {}; 390bufferInfo .range = VK_WHOLE_SIZE ; 391 392if (resourceViews [i ]) 393 { 394auto boundViewType = static_cast < ResourceViewImpl *> (resourceViews [i ].Ptr ())-> m_type ; 395if (boundViewType == ResourceViewImpl ::ViewType ::PlainBuffer ) 396 { 397auto bufferView = static_cast < PlainBufferResourceViewImpl *> (resourceViews [i ].Ptr ()); 398bufferInfo .buffer = bufferView -> m_buffer -> m_buffer .m_buffer ; 399bufferInfo .offset = bufferView -> offset ; 400bufferInfo .range = bufferView -> size ; 401 } 402 } 403 404VkWriteDescriptorSet write = {}; 405write .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ; 406write .descriptorCount = 1 ; 407write .descriptorType = descriptorType ; 408write .dstArrayElement = uint32_t (i ); 409write .dstBinding = offset .binding ; 410write .dstSet = descriptorSet ; 411write .pBufferInfo = & bufferInfo ; 412 413writeDescriptor (context ,write ); 414 } 415} 416 417void ShaderObjectImpl ::writeTexelBufferDescriptor ( 418RootBindingContext & context , 419BindingOffset const & offset , 420VkDescriptorType descriptorType , 421ArrayView < RefPtr < ResourceViewInternalBase >> resourceViews ) 422{ 423auto descriptorSet = (* context .descriptorSets )[offset .bindingSet ]; 424 425Index count = resourceViews .getCount (); 426for (Index i = 0 ;i < count ;++ i ) 427 { 428VkBufferView bufferView = VK_NULL_HANDLE ; 429if (resourceViews [i ]) 430 { 431auto boundViewType = static_cast < ResourceViewImpl *> (resourceViews [i ].Ptr ())-> m_type ; 432if (boundViewType == ResourceViewImpl ::ViewType ::TexelBuffer ) 433 { 434auto resourceView = 435static_cast < TexelBufferResourceViewImpl *> (resourceViews [i ].Ptr ()); 436bufferView = resourceView -> m_view ; 437 } 438 } 439VkWriteDescriptorSet write = {}; 440write .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ; 441write .descriptorType = descriptorType ; 442write .dstArrayElement = uint32_t (i ); 443write .dstBinding = offset .binding ; 444write .dstSet = descriptorSet ; 445write .descriptorCount = 1 ; 446write .pTexelBufferView = & bufferView ; 447writeDescriptor (context ,write ); 448 } 449} 450 451void ShaderObjectImpl ::writeTextureSamplerDescriptor ( 452RootBindingContext & context , 453BindingOffset const & offset , 454VkDescriptorType descriptorType , 455ArrayView < CombinedTextureSamplerSlot > slots ) 456{ 457auto descriptorSet = (* context .descriptorSets )[offset .bindingSet ]; 458 459Index count = slots .getCount (); 460for (Index i = 0 ;i < count ;++ i ) 461 { 462auto texture = slots [i ].textureView ; 463auto sampler = slots [i ].sampler ; 464VkDescriptorImageInfo imageInfo = {}; 465if (texture ) 466 { 467imageInfo .imageView = texture -> m_view ; 468imageInfo .imageLayout = texture -> m_layout ; 469 } 470if (sampler ) 471 { 472imageInfo .sampler = sampler -> m_sampler ; 473 } 474else 475 { 476imageInfo .sampler = context .device -> m_defaultSampler ; 477 } 478 479VkWriteDescriptorSet write = {}; 480write .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ; 481write .descriptorCount = 1 ; 482write .descriptorType = descriptorType ; 483write .dstArrayElement = uint32_t (i ); 484write .dstBinding = offset .binding ; 485write .dstSet = descriptorSet ; 486write .pImageInfo = & imageInfo ; 487 488writeDescriptor (context ,write ); 489 } 490} 491 492void ShaderObjectImpl ::writeAccelerationStructureDescriptor ( 493RootBindingContext & context , 494BindingOffset const & offset , 495VkDescriptorType descriptorType , 496ArrayView < RefPtr < ResourceViewInternalBase >> resourceViews ) 497{ 498auto descriptorSet = (* context .descriptorSets )[offset .bindingSet ]; 499 500Index count = resourceViews .getCount (); 501for (Index i = 0 ;i < count ;++ i ) 502 { 503auto accelerationStructure = 504static_cast < AccelerationStructureImpl *> (resourceViews [i ].Ptr ()); 505VkWriteDescriptorSetAccelerationStructureKHR writeAS = {}; 506writeAS .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR ; 507if (accelerationStructure ) 508 { 509writeAS .accelerationStructureCount = 1 ; 510writeAS .pAccelerationStructures = & accelerationStructure -> m_vkHandle ; 511 } 512else 513 { 514// The Vulkan spec states: If the nullDescriptor feature is not enabled, each element of 515// pAccelerationStructures must not be VK_NULL_HANDLE 516SLANG_ASSERT ( 517context .device -> m_api .m_extendedFeatures .robustness2Features .nullDescriptor ); 518 519static const VkAccelerationStructureKHR nullHandle = VK_NULL_HANDLE ; 520writeAS .accelerationStructureCount = 1 ; 521writeAS .pAccelerationStructures = & nullHandle ; 522 } 523VkWriteDescriptorSet write = {}; 524write .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ; 525write .descriptorCount = 1 ; 526write .descriptorType = descriptorType ; 527write .dstArrayElement = uint32_t (i ); 528write .dstBinding = offset .binding ; 529write .dstSet = descriptorSet ; 530write .pNext = & writeAS ; 531writeDescriptor (context ,write ); 532 } 533} 534 535void ShaderObjectImpl ::writeTextureDescriptor ( 536RootBindingContext & context , 537BindingOffset const & offset , 538VkDescriptorType descriptorType , 539ArrayView < RefPtr < ResourceViewInternalBase >> resourceViews ) 540{ 541auto descriptorSet = (* context .descriptorSets )[offset .bindingSet ]; 542 543Index count = resourceViews .getCount (); 544for (Index i = 0 ;i < count ;++ i ) 545 { 546VkDescriptorImageInfo imageInfo = {}; 547if (resourceViews [i ]) 548 { 549auto boundViewType = static_cast < ResourceViewImpl *> (resourceViews [i ].Ptr ())-> m_type ; 550if (boundViewType == ResourceViewImpl ::ViewType ::Texture ) 551 { 552auto texture = static_cast < TextureResourceViewImpl *> (resourceViews [i ].Ptr ()); 553imageInfo .imageView = texture -> m_view ; 554imageInfo .imageLayout = texture -> m_layout ; 555 } 556 } 557imageInfo .sampler = 0 ; 558 559VkWriteDescriptorSet write = {}; 560write .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ; 561write .descriptorCount = 1 ; 562write .descriptorType = descriptorType ; 563write .dstArrayElement = uint32_t (i ); 564write .dstBinding = offset .binding ; 565write .dstSet = descriptorSet ; 566write .pImageInfo = & imageInfo ; 567 568writeDescriptor (context ,write ); 569 } 570} 571 572void ShaderObjectImpl ::writeSamplerDescriptor ( 573RootBindingContext & context , 574BindingOffset const & offset , 575VkDescriptorType descriptorType , 576ArrayView < RefPtr < SamplerStateImpl >> samplers ) 577{ 578auto descriptorSet = (* context .descriptorSets )[offset .bindingSet ]; 579 580Index count = samplers .getCount (); 581for (Index i = 0 ;i < count ;++ i ) 582 { 583auto sampler = samplers [i ]; 584VkDescriptorImageInfo imageInfo = {}; 585imageInfo .imageView = 0 ; 586imageInfo .imageLayout = VK_IMAGE_LAYOUT_GENERAL ; 587if (sampler ) 588 { 589imageInfo .sampler = sampler -> m_sampler ; 590 } 591else 592 { 593imageInfo .sampler = context .device -> m_defaultSampler ; 594 } 595 596VkWriteDescriptorSet write = {}; 597write .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET ; 598write .descriptorCount = 1 ; 599write .descriptorType = descriptorType ; 600write .dstArrayElement = uint32_t (i ); 601write .dstBinding = offset .binding ; 602write .dstSet = descriptorSet ; 603write .pImageInfo = & imageInfo ; 604 605writeDescriptor (context ,write ); 606 } 607} 608 609bool ShaderObjectImpl ::shouldAllocateConstantBuffer (TransientResourceHeapImpl * transientHeap ) 610{ 611return m_isConstantBufferDirty || m_constantBufferTransientHeap != transientHeap || 612m_constantBufferTransientHeapVersion != transientHeap -> getVersion (); 613} 614 615Result ShaderObjectImpl ::_ensureOrdinaryDataBufferCreatedIfNeeded ( 616PipelineCommandEncoder * encoder , 617ShaderObjectLayoutImpl * specializedLayout ) 618{ 619// If data has been changed since last allocation/filling of constant buffer, 620// we will need to allocate a new one. 621// 622if (!shouldAllocateConstantBuffer (encoder -> m_commandBuffer -> m_transientHeap )) 623 { 624return SLANG_OK ; 625 } 626m_isConstantBufferDirty = false; 627m_constantBufferTransientHeap = encoder -> m_commandBuffer -> m_transientHeap ; 628m_constantBufferTransientHeapVersion = encoder -> m_commandBuffer -> m_transientHeap -> getVersion (); 629 630m_constantBufferSize = specializedLayout -> getTotalOrdinaryDataSize (); 631if (m_constantBufferSize == 0 ) 632 { 633return SLANG_OK ; 634 } 635 636// Once we have computed how large the buffer should be, we can allocate 637// it from the transient resource heap. 638// 639SLANG_RETURN_ON_FAIL (encoder -> m_commandBuffer -> m_transientHeap -> allocateConstantBuffer ( 640m_constantBufferSize , 641m_constantBuffer , 642m_constantBufferOffset )); 643 644// Once the buffer is allocated, we can use `_writeOrdinaryData` to fill it in. 645// 646// Note that `_writeOrdinaryData` is potentially recursive in the case 647// where this object contains interface/existential-type fields, so we 648// don't need or want to inline it into this call site. 649// 650SLANG_RETURN_ON_FAIL (_writeOrdinaryData ( 651encoder , 652m_constantBuffer , 653m_constantBufferOffset , 654m_constantBufferSize , 655specializedLayout )); 656 657return SLANG_OK ; 658} 659 660Result ShaderObjectImpl ::bindAsValue ( 661PipelineCommandEncoder * encoder , 662RootBindingContext & context , 663BindingOffset const & offset , 664ShaderObjectLayoutImpl * specializedLayout ) 665{ 666// We start by iterating over the "simple" (non-sub-object) binding 667// ranges and writing them to the descriptor sets that are being 668// passed down. 669// 670for (auto bindingRangeInfo :specializedLayout -> getBindingRanges ()) 671 { 672BindingOffset rangeOffset = offset ; 673 674auto baseIndex = bindingRangeInfo .baseIndex ; 675auto count = (uint32_t )bindingRangeInfo .count ; 676switch (bindingRangeInfo .bindingType ) 677 { 678case slang::BindingType ::ConstantBuffer : 679case slang::BindingType ::ParameterBlock : 680case slang::BindingType ::ExistentialValue : 681break ; 682 683case slang::BindingType ::Texture : 684rangeOffset .bindingSet += bindingRangeInfo .setOffset ; 685rangeOffset .binding += bindingRangeInfo .bindingOffset ; 686writeTextureDescriptor ( 687context , 688rangeOffset , 689VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE , 690m_resourceViews .getArrayView (baseIndex ,count )); 691break ; 692case slang::BindingType ::MutableTexture : 693rangeOffset .bindingSet += bindingRangeInfo .setOffset ; 694rangeOffset .binding += bindingRangeInfo .bindingOffset ; 695writeTextureDescriptor ( 696context , 697rangeOffset , 698VK_DESCRIPTOR_TYPE_STORAGE_IMAGE , 699m_resourceViews .getArrayView (baseIndex ,count )); 700break ; 701case slang::BindingType ::CombinedTextureSampler : 702rangeOffset .bindingSet += bindingRangeInfo .setOffset ; 703rangeOffset .binding += bindingRangeInfo .bindingOffset ; 704writeTextureSamplerDescriptor ( 705context , 706rangeOffset , 707VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER , 708m_combinedTextureSamplers .getArrayView (baseIndex ,count )); 709break ; 710 711case slang::BindingType ::Sampler : 712rangeOffset .bindingSet += bindingRangeInfo .setOffset ; 713rangeOffset .binding += bindingRangeInfo .bindingOffset ; 714writeSamplerDescriptor ( 715context , 716rangeOffset , 717VK_DESCRIPTOR_TYPE_SAMPLER , 718m_samplers .getArrayView (baseIndex ,count )); 719break ; 720 721case slang::BindingType ::RawBuffer : 722case slang::BindingType ::MutableRawBuffer : 723rangeOffset .bindingSet += bindingRangeInfo .setOffset ; 724rangeOffset .binding += bindingRangeInfo .bindingOffset ; 725writePlainBufferDescriptor ( 726context , 727rangeOffset , 728VK_DESCRIPTOR_TYPE_STORAGE_BUFFER , 729m_resourceViews .getArrayView (baseIndex ,count )); 730break ; 731 732case slang::BindingType ::TypedBuffer : 733rangeOffset .bindingSet += bindingRangeInfo .setOffset ; 734rangeOffset .binding += bindingRangeInfo .bindingOffset ; 735writeTexelBufferDescriptor ( 736context , 737rangeOffset , 738VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER , 739m_resourceViews .getArrayView (baseIndex ,count )); 740break ; 741case slang::BindingType ::MutableTypedBuffer : 742rangeOffset .bindingSet += bindingRangeInfo .setOffset ; 743rangeOffset .binding += bindingRangeInfo .bindingOffset ; 744writeTexelBufferDescriptor ( 745context , 746rangeOffset , 747VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER , 748m_resourceViews .getArrayView (baseIndex ,count )); 749break ; 750case slang::BindingType ::RayTracingAccelerationStructure : 751rangeOffset .bindingSet += bindingRangeInfo .setOffset ; 752rangeOffset .binding += bindingRangeInfo .bindingOffset ; 753writeAccelerationStructureDescriptor ( 754context , 755rangeOffset , 756VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR , 757m_resourceViews .getArrayView (baseIndex ,count )); 758break ; 759case slang::BindingType ::VaryingInput : 760case slang::BindingType ::VaryingOutput : 761break ; 762 763default : 764SLANG_ASSERT (!"unsupported binding type" ); 765return SLANG_FAIL ; 766break ; 767 } 768 } 769 770// Once we've handled the simple binding ranges, we move on to the 771// sub-object ranges, which are generally more involved. 772// 773for (auto const & subObjectRange :specializedLayout -> getSubObjectRanges ()) 774 { 775auto const & bindingRangeInfo = 776specializedLayout -> getBindingRange (subObjectRange .bindingRangeIndex ); 777auto count = bindingRangeInfo .count ; 778auto subObjectIndex = bindingRangeInfo .subObjectIndex ; 779 780auto subObjectLayout = subObjectRange .layout ; 781 782// The starting offset to use for the sub-object 783// has already been computed and stored as part 784// of the layout, so we can get to the starting 785// offset for the range easily. 786// 787BindingOffset rangeOffset = offset ; 788rangeOffset += subObjectRange .offset ; 789 790BindingOffset rangeStride = subObjectRange .stride ; 791 792switch (bindingRangeInfo .bindingType ) 793 { 794case slang::BindingType ::ConstantBuffer : 795 { 796BindingOffset objOffset = rangeOffset ; 797for (Index i = 0 ;i < count ;++ i ) 798 { 799// Binding a constant buffer sub-object is simple enough: 800// we just call `bindAsConstantBuffer` on it to bind 801// the ordinary data buffer (if needed) and any other 802// bindings it recursively contains. 803// 804ShaderObjectImpl * subObject = m_objects [subObjectIndex + i ]; 805subObject -> bindAsConstantBuffer (encoder ,context ,objOffset ,subObjectLayout ); 806 807// When dealing with arrays of sub-objects, we need to make 808// sure to increment the offset for each subsequent object 809// by the appropriate stride. 810// 811objOffset += rangeStride ; 812 } 813 } 814break ; 815case slang::BindingType ::ParameterBlock : 816 { 817BindingOffset objOffset = rangeOffset ; 818for (Index i = 0 ;i < count ;++ i ) 819 { 820// The case for `ParameterBlock<X>` is not that different 821// from `ConstantBuffer<X>`, except that we call `bindAsParameterBlock` 822// instead (understandably). 823// 824ShaderObjectImpl * subObject = m_objects [subObjectIndex + i ]; 825subObject -> bindAsParameterBlock (encoder ,context ,objOffset ,subObjectLayout ); 826 } 827 } 828break ; 829 830case slang::BindingType ::ExistentialValue : 831// Interface/existential-type sub-object ranges are the most complicated case. 832// 833// First, we can only bind things if we have static specialization information 834// to work with, which is exactly the case where `subObjectLayout` will be 835// non-null. 836// 837if (subObjectLayout ) 838 { 839// Second, the offset where we want to start binding for existential-type 840// ranges is a bit different, because we don't wnat to bind at the "primary" 841// offset that got passed down, but instead at the "pending" offset. 842// 843// For the purposes of nested binding, what used to be the pending offset 844// will now be used as the primary offset. 845// 846SimpleBindingOffset objOffset = rangeOffset .pending ; 847SimpleBindingOffset objStride = rangeStride .pending ; 848for (Index i = 0 ;i < count ;++ i ) 849 { 850// An existential-type sub-object is always bound just as a value, 851// which handles its nested bindings and descriptor sets, but 852// does not deal with ordianry data. The ordinary data should 853// have been handled as part of the buffer for a parent object 854// already. 855// 856ShaderObjectImpl * subObject = m_objects [subObjectIndex + i ]; 857subObject 858-> bindAsValue (encoder ,context ,BindingOffset (objOffset ),subObjectLayout ); 859objOffset += objStride ; 860 } 861 } 862break ; 863case slang::BindingType ::RawBuffer : 864case slang::BindingType ::MutableRawBuffer : 865// No action needed for sub-objects bound though a `StructuredBuffer`. 866break ; 867default : 868SLANG_ASSERT (!"unsupported sub-object type" ); 869return SLANG_FAIL ; 870break ; 871 } 872 } 873 874return SLANG_OK ; 875} 876 877Result ShaderObjectImpl ::allocateDescriptorSets ( 878PipelineCommandEncoder * encoder , 879RootBindingContext & context , 880BindingOffset const & offset , 881ShaderObjectLayoutImpl * specializedLayout ) 882{ 883assert (specializedLayout -> getOwnDescriptorSets ().getCount () <=1 ); 884// The number of sets to allocate and their layouts was already pre-computed 885// as part of the shader object layout, so we use that information here. 886// 887for (auto descriptorSetInfo :specializedLayout -> getOwnDescriptorSets ()) 888 { 889auto descriptorSetHandle = 890context .descriptorSetAllocator -> allocate (descriptorSetInfo .descriptorSetLayout ).handle ; 891 892// For each set, we need to write it into the set of descriptor sets 893// being used for binding. This is done both so that other steps 894// in binding can find the set to fill it in, but also so that 895// we can bind all the descriptor sets to the pipeline when the 896// time comes. 897// 898 (* context .descriptorSets ).add (descriptorSetHandle ); 899 } 900 901return SLANG_OK ; 902} 903 904Result ShaderObjectImpl ::bindAsParameterBlock ( 905PipelineCommandEncoder * encoder , 906RootBindingContext & context , 907BindingOffset const & inOffset , 908ShaderObjectLayoutImpl * specializedLayout ) 909{ 910// Because we are binding into a nested parameter block, 911// any texture/buffer/sampler bindings will now want to 912// write into the sets we allocate for this object and 913// not the sets for any parent object(s). 914// 915BindingOffset offset = inOffset ; 916offset .bindingSet = (uint32_t )context .descriptorSets -> getCount (); 917offset .binding = 0 ; 918 919// TODO: We should also be writing to `offset.pending` here, 920// because any resource/sampler bindings related to "pending" 921// data should *also* be writing into the chosen set. 922// 923// The challenge here is that we need to compute the right 924// value for `offset.pending.binding`, so that it writes after 925// all the other bindings. 926 927// Writing the bindings for a parameter block is relatively easy: 928// we just need to allocate the descriptor set(s) needed for this 929// object and then fill it in like a `ConstantBuffer<X>`. 930// 931SLANG_RETURN_ON_FAIL (allocateDescriptorSets (encoder ,context ,offset ,specializedLayout )); 932 933assert (offset .bindingSet < (uint32_t )context .descriptorSets -> getCount ()); 934SLANG_RETURN_ON_FAIL (bindAsConstantBuffer (encoder ,context ,offset ,specializedLayout )); 935 936return SLANG_OK ; 937} 938 939Result ShaderObjectImpl ::bindOrdinaryDataBufferIfNeeded ( 940PipelineCommandEncoder * encoder , 941RootBindingContext & context , 942BindingOffset & ioOffset , 943ShaderObjectLayoutImpl * specializedLayout ) 944{ 945// We start by ensuring that the buffer is created, if it is needed. 946// 947SLANG_RETURN_ON_FAIL (_ensureOrdinaryDataBufferCreatedIfNeeded (encoder ,specializedLayout )); 948 949// If we did indeed need/create a buffer, then we must bind it into 950// the given `descriptorSet` and update the base range index for 951// subsequent binding operations to account for it. 952// 953if (m_constantBuffer && m_constantBufferSize > 0 ) 954 { 955auto bufferImpl = static_cast < BufferResourceImpl *> (m_constantBuffer ); 956writeBufferDescriptor ( 957context , 958ioOffset , 959VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER , 960bufferImpl , 961m_constantBufferOffset , 962m_constantBufferSize ); 963ioOffset .binding ++ ; 964 } 965 966return SLANG_OK ; 967} 968 969Result ShaderObjectImpl ::bindAsConstantBuffer ( 970PipelineCommandEncoder * encoder , 971RootBindingContext & context , 972BindingOffset const & inOffset , 973ShaderObjectLayoutImpl * specializedLayout ) 974{ 975// To bind an object as a constant buffer, we first 976// need to bind its ordinary data (if any) into an 977// ordinary data buffer, and then bind it as a "value" 978// which handles any of its recursively-contained bindings. 979// 980// The one detail is taht when binding the ordinary data 981// buffer we need to adjust the `binding` index used for 982// subsequent operations based on whether or not an ordinary 983// data buffer was used (and thus consumed a `binding`). 984// 985BindingOffset offset = inOffset ; 986SLANG_RETURN_ON_FAIL ( 987bindOrdinaryDataBufferIfNeeded (encoder ,context ,/*inout*/ offset ,specializedLayout )); 988SLANG_RETURN_ON_FAIL (bindAsValue (encoder ,context ,offset ,specializedLayout )); 989return SLANG_OK ; 990} 991 992Result ShaderObjectImpl ::_getSpecializedLayout (ShaderObjectLayoutImpl ** outLayout ) 993{ 994if (!m_specializedLayout ) 995 { 996SLANG_RETURN_ON_FAIL (_createSpecializedLayout (m_specializedLayout .writeRef ())); 997 } 998returnRefPtr (outLayout ,m_specializedLayout ); 999return SLANG_OK ; 1000} 1001 1002Result ShaderObjectImpl ::_createSpecializedLayout (ShaderObjectLayoutImpl ** outLayout ) 1003{ 1004ExtendedShaderObjectType extendedType ; 1005SLANG_RETURN_ON_FAIL (getSpecializedShaderObjectType (& extendedType )); 1006 1007auto device = getDevice (); 1008RefPtr < ShaderObjectLayoutImpl > layout ; 1009SLANG_RETURN_ON_FAIL (device -> getShaderObjectLayout ( 1010m_layout -> m_slangSession , 1011extendedType .slangType , 1012m_layout -> getContainerType (), 1013 (ShaderObjectLayoutBase ** )layout .writeRef ())); 1014 1015returnRefPtrMove (outLayout ,layout ); 1016return SLANG_OK ; 1017} 1018 1019Result EntryPointShaderObject ::create ( 1020IDevice * device , 1021EntryPointLayout * layout , 1022EntryPointShaderObject ** outShaderObject ) 1023{ 1024RefPtr < EntryPointShaderObject > object = new EntryPointShaderObject (); 1025SLANG_RETURN_ON_FAIL (object -> init (device ,layout )); 1026 1027returnRefPtrMove (outShaderObject ,object ); 1028return SLANG_OK ; 1029} 1030 1031EntryPointLayout * EntryPointShaderObject ::getLayout () 1032{ 1033return static_cast < EntryPointLayout *> (m_layout .Ptr ()); 1034} 1035 1036Result EntryPointShaderObject ::bindAsEntryPoint ( 1037PipelineCommandEncoder * encoder , 1038RootBindingContext & context , 1039BindingOffset const & inOffset , 1040EntryPointLayout * layout ) 1041{ 1042BindingOffset offset = inOffset ; 1043 1044// Any ordinary data in an entry point is assumed to be allocated 1045// as a push-constant range. 1046// 1047// TODO: Can we make this operation not bake in that assumption? 1048// 1049// TODO: Can/should this function be renamed as just `bindAsPushConstantBuffer`? 1050// 1051if (m_data .getCount ()) 1052 { 1053// The index of the push constant range to bind should be 1054// passed down as part of the `offset`, and we will increment 1055// it here so that any further recursively-contained push-constant 1056// ranges use the next index. 1057// 1058auto pushConstantRangeIndex = offset .pushConstantRange ++ ; 1059 1060// Information about the push constant ranges (including offsets 1061// and stage flags) was pre-computed for the entire program and 1062// stored on the binding context. 1063// 1064auto const & pushConstantRange = context .pushConstantRanges [pushConstantRangeIndex ]; 1065 1066// We expect that the size of the range as reflected matches the 1067// amount of ordinary data stored on this object. 1068// 1069// TODO: This would not be the case if specialization for interface-type 1070// parameters led to the entry point having "pending" ordinary data. 1071// 1072SLANG_ASSERT (pushConstantRange .size == (uint32_t )m_data .getCount ()); 1073 1074auto pushConstantData = m_data .getBuffer (); 1075 1076encoder -> m_api -> vkCmdPushConstants ( 1077encoder -> m_commandBuffer -> m_commandBuffer , 1078context .pipelineLayout , 1079pushConstantRange .stageFlags , 1080pushConstantRange .offset , 1081pushConstantRange .size , 1082pushConstantData ); 1083 } 1084 1085// Any remaining bindings in the object can be handled through the 1086// "value" case. 1087// 1088SLANG_RETURN_ON_FAIL (bindAsValue (encoder ,context ,offset ,layout )); 1089return SLANG_OK ; 1090} 1091 1092Result EntryPointShaderObject ::init (IDevice * device ,EntryPointLayout * layout ) 1093{ 1094SLANG_RETURN_ON_FAIL (Super ::init (device ,layout )); 1095return SLANG_OK ; 1096} 1097 1098RootShaderObjectLayout * RootShaderObjectImpl ::getLayout () 1099{ 1100return static_cast < RootShaderObjectLayout *> (m_layout .Ptr ()); 1101} 1102 1103RootShaderObjectLayout * RootShaderObjectImpl ::getSpecializedLayout () 1104{ 1105RefPtr < ShaderObjectLayoutImpl > specializedLayout ; 1106_getSpecializedLayout (specializedLayout .writeRef ()); 1107return static_cast < RootShaderObjectLayout *> (m_specializedLayout .Ptr ()); 1108} 1109 1110List < RefPtr < EntryPointShaderObject >> const & RootShaderObjectImpl ::getEntryPoints ()const 1111{ 1112return m_entryPoints ; 1113} 1114 1115GfxCount RootShaderObjectImpl ::getEntryPointCount () 1116{ 1117return (GfxCount )m_entryPoints .getCount (); 1118} 1119 1120Result RootShaderObjectImpl ::getEntryPoint (GfxIndex index ,IShaderObject ** outEntryPoint ) 1121{ 1122returnComPtr (outEntryPoint ,m_entryPoints [index ]); 1123return SLANG_OK ; 1124} 1125 1126Result RootShaderObjectImpl ::copyFrom (IShaderObject * object ,ITransientResourceHeap * transientHeap ) 1127{ 1128SLANG_RETURN_ON_FAIL (Super ::copyFrom (object ,transientHeap )); 1129if (auto srcObj = dynamic_cast < MutableRootShaderObject *> (object )) 1130 { 1131for (Index i = 0 ;i < srcObj -> m_entryPoints .getCount ();i ++ ) 1132 { 1133m_entryPoints [i ]-> copyFrom (srcObj -> m_entryPoints [i ],transientHeap ); 1134 } 1135return SLANG_OK ; 1136 } 1137return SLANG_FAIL ; 1138} 1139 1140Result RootShaderObjectImpl ::bindAsRoot ( 1141PipelineCommandEncoder * encoder , 1142RootBindingContext & context , 1143RootShaderObjectLayout * layout ) 1144{ 1145BindingOffset offset = {}; 1146offset .pending = layout -> getPendingDataOffset (); 1147 1148// Note: the operations here are quite similar to what `bindAsParameterBlock` does. 1149// The key difference in practice is that we do *not* make use of the adjustment 1150// that `bindOrdinaryDataBufferIfNeeded` applied to the offset passed into it. 1151// 1152// The reason for this difference in behavior is that the layout information 1153// for root shader parameters is in practice *already* offset appropriately 1154// (so that it ends up using absolute offsets). 1155// 1156// TODO: One more wrinkle here is that the `ordinaryDataBufferOffset` below 1157// might not be correct if `binding=0,set=0` was already claimed via explicit 1158// binding information. We should really be getting the offset information for 1159// the ordinary data buffer directly from the reflection information for 1160// the global scope. 1161 1162SLANG_RETURN_ON_FAIL (allocateDescriptorSets (encoder ,context ,offset ,layout )); 1163 1164BindingOffset ordinaryDataBufferOffset = offset ; 1165SLANG_RETURN_ON_FAIL ( 1166bindOrdinaryDataBufferIfNeeded (encoder ,context ,ordinaryDataBufferOffset ,layout )); 1167 1168SLANG_RETURN_ON_FAIL (bindAsValue (encoder ,context ,offset ,layout )); 1169 1170auto entryPointCount = layout -> getEntryPoints ().getCount (); 1171for (Index i = 0 ;i < entryPointCount ;++ i ) 1172 { 1173auto entryPoint = m_entryPoints [i ]; 1174auto const & entryPointInfo = layout -> getEntryPoint (i ); 1175 1176// Note: we do *not* need to add the entry point offset 1177// information to the global `offset` because the 1178// `RootShaderObjectLayout` has already baked any offsets 1179// from the global layout into the `entryPointInfo`. 1180 1181entryPoint 1182-> bindAsEntryPoint (encoder ,context ,entryPointInfo .offset ,entryPointInfo .layout ); 1183 } 1184 1185return SLANG_OK ; 1186} 1187 1188Result RootShaderObjectImpl ::collectSpecializationArgs (ExtendedShaderObjectTypeList & args ) 1189{ 1190SLANG_RETURN_ON_FAIL (ShaderObjectImpl ::collectSpecializationArgs (args )); 1191for (auto & entryPoint :m_entryPoints ) 1192 { 1193SLANG_RETURN_ON_FAIL (entryPoint -> collectSpecializationArgs (args )); 1194 } 1195return SLANG_OK ; 1196} 1197 1198Result RootShaderObjectImpl ::init (IDevice * device ,RootShaderObjectLayout * layout ) 1199{ 1200SLANG_RETURN_ON_FAIL (Super ::init (device ,layout )); 1201m_specializedLayout = nullptr ; 1202m_entryPoints .clear (); 1203for (auto entryPointInfo :layout -> getEntryPoints ()) 1204 { 1205RefPtr < EntryPointShaderObject > entryPoint ; 1206SLANG_RETURN_ON_FAIL ( 1207EntryPointShaderObject ::create (device ,entryPointInfo .layout ,entryPoint .writeRef ())); 1208m_entryPoints .add (entryPoint ); 1209 } 1210 1211return SLANG_OK ; 1212} 1213 1214Result RootShaderObjectImpl ::_createSpecializedLayout (ShaderObjectLayoutImpl ** outLayout ) 1215{ 1216ExtendedShaderObjectTypeList specializationArgs ; 1217SLANG_RETURN_ON_FAIL (collectSpecializationArgs (specializationArgs )); 1218 1219// Note: There is an important policy decision being made here that we need 1220// to approach carefully. 1221// 1222// We are doing two different things that affect the layout of a program: 1223// 1224// 1. We are *composing* one or more pieces of code (notably the shared global/module 1225// stuff and the per-entry-point stuff). 1226// 1227// 2. We are *specializing* code that includes generic/existential parameters 1228// to concrete types/values. 1229// 1230// We need to decide the relative *order* of these two steps, because of how it impacts 1231// layout. The layout for `specialize(compose(A,B), X, Y)` is potentially different 1232// form that of `compose(specialize(A,X), speciealize(B,Y))`, even when both are 1233// semantically equivalent programs. 1234// 1235// Right now we are using the first option: we are first generating a full composition 1236// of all the code we plan to use (global scope plus all entry points), and then 1237// specializing it to the concatenated specialization argumenst for all of that. 1238// 1239// In some cases, though, this model isn't appropriate. For example, when dealing with 1240// ray-tracing shaders and local root signatures, we really want the parameters of each 1241// entry point (actually, each entry-point *group*) to be allocated distinct storage, 1242// which really means we want to compute something like: 1243// 1244// SpecializedGlobals = specialize(compose(ModuleA, ModuleB, ...), X, Y, ...) 1245// 1246// SpecializedEP1 = compose(SpecializedGlobals, specialize(EntryPoint1, T, U, ...)) 1247// SpecializedEP2 = compose(SpecializedGlobals, specialize(EntryPoint2, A, B, ...)) 1248// 1249// Note how in this case all entry points agree on the layout for the shared/common 1250// parmaeters, but their layouts are also independent of one another. 1251// 1252// Furthermore, in this example, loading another entry point into the system would not 1253// rquire re-computing the layouts (or generated kernel code) for any of the entry 1254// points that had already been loaded (in contrast to a compose-then-specialize 1255// approach). 1256// 1257ComPtr < slang::IComponentType > specializedComponentType ; 1258ComPtr < slang::IBlob > diagnosticBlob ; 1259auto result = getLayout ()-> getSlangProgram ()-> specialize ( 1260specializationArgs .components .getArrayView ().getBuffer (), 1261specializationArgs .getCount (), 1262specializedComponentType .writeRef (), 1263diagnosticBlob .writeRef ()); 1264 1265// TODO: print diagnostic message via debug output interface. 1266 1267if (result != SLANG_OK ) 1268return result ; 1269 1270auto slangSpecializedLayout = specializedComponentType -> getLayout (); 1271RefPtr < RootShaderObjectLayout > specializedLayout ; 1272RootShaderObjectLayout ::create ( 1273static_cast < DeviceImpl *> (getRenderer ()), 1274specializedComponentType , 1275slangSpecializedLayout , 1276specializedLayout .writeRef ()); 1277 1278// Note: Computing the layout for the specialized program will have also computed 1279// the layouts for the entry points, and we really need to attach that information 1280// to them so that they don't go and try to compute their own specializations. 1281// 1282// TODO: Well, if we move to the specialization model described above then maybe 1283// we *will* want entry points to do their own specialization work... 1284// 1285auto entryPointCount = m_entryPoints .getCount (); 1286for (Index i = 0 ;i < entryPointCount ;++ i ) 1287 { 1288auto entryPointInfo = specializedLayout -> getEntryPoint (i ); 1289auto entryPointVars = m_entryPoints [i ]; 1290 1291entryPointVars -> m_specializedLayout = entryPointInfo .layout ; 1292 } 1293 1294returnRefPtrMove (outLayout ,specializedLayout ); 1295return SLANG_OK ; 1296} 1297 1298}// namespace vk 1299}// namespace gfx