yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
941f07040
master
1// d3d12-shader-object-layout.cpp 2#include "d3d12-shader-object-layout.h" 3 4#include "d3d12-device.h" 5 6namespace gfx 7{ 8namespace d3d12 9{ 10 11using namespace Slang ; 12 13ShaderObjectLayoutImpl ::SubObjectRangeOffset ::SubObjectRangeOffset ( 14 slang::VariableLayoutReflection * varLayout ) 15{ 16if (auto pendingLayout = varLayout -> getPendingDataLayout ()) 17 { 18pendingOrdinaryData = (uint32_t )pendingLayout -> getOffset (SLANG_PARAMETER_CATEGORY_UNIFORM ); 19 } 20} 21 22ShaderObjectLayoutImpl ::SubObjectRangeStride ::SubObjectRangeStride ( 23 slang::TypeLayoutReflection * typeLayout ) 24{ 25if (auto pendingLayout = typeLayout -> getPendingDataTypeLayout ()) 26 { 27pendingOrdinaryData = (uint32_t )pendingLayout -> getSize (SLANG_PARAMETER_CATEGORY_UNIFORM ); 28 } 29} 30 31bool ShaderObjectLayoutImpl ::isBindingRangeRootParameter ( 32SlangSession * globalSession , 33const char * rootParameterAttributeName , 34 slang::TypeLayoutReflection * typeLayout , 35Index bindingRangeIndex ) 36{ 37bool isRootParameter = false; 38if (rootParameterAttributeName ) 39 { 40if (auto leafVariable = typeLayout -> getBindingRangeLeafVariable (bindingRangeIndex )) 41 { 42if (leafVariable -> findAttributeByName (globalSession ,rootParameterAttributeName )) 43 { 44isRootParameter = true; 45 } 46 } 47 } 48return isRootParameter ; 49} 50 51Result ShaderObjectLayoutImpl ::createForElementType ( 52RendererBase * renderer , 53 slang::ISession * session , 54 slang::TypeLayoutReflection * elementType , 55ShaderObjectLayoutImpl ** outLayout ) 56{ 57Builder builder (renderer ,session ); 58builder .setElementTypeLayout (elementType ); 59return builder .build (outLayout ); 60} 61 62Result ShaderObjectLayoutImpl ::init (Builder * builder ) 63{ 64auto renderer = builder -> m_renderer ; 65 66initBase (renderer ,builder -> m_session ,builder -> m_elementTypeLayout ); 67 68m_containerType = builder -> m_containerType ; 69 70m_bindingRanges = _Move (builder -> m_bindingRanges ); 71m_subObjectRanges = _Move (builder -> m_subObjectRanges ); 72m_rootParamsInfo = _Move (builder -> m_rootParamsInfo ); 73 74m_ownCounts = builder -> m_ownCounts ; 75m_totalCounts = builder -> m_totalCounts ; 76m_subObjectCount = builder -> m_subObjectCount ; 77m_childRootParameterCount = builder -> m_childRootParameterCount ; 78m_totalOrdinaryDataSize = builder -> m_totalOrdinaryDataSize ; 79 80return SLANG_OK ; 81} 82 83Result ShaderObjectLayoutImpl ::Builder ::setElementTypeLayout ( 84 slang::TypeLayoutReflection * typeLayout ) 85{ 86typeLayout = _unwrapParameterGroups (typeLayout ,m_containerType ); 87m_elementTypeLayout = typeLayout ; 88 89// If the type contains any ordinary data, then we must reserve a buffer 90// descriptor to hold it when binding as a parameter block. 91// 92m_totalOrdinaryDataSize = (uint32_t )typeLayout -> getSize (); 93if (m_totalOrdinaryDataSize != 0 ) 94 { 95m_ownCounts .resource ++ ; 96 } 97 98// We will scan over the reflected Slang binding ranges and add them 99// to our array. There are two main things we compute along the way: 100// 101// * For each binding range we compute a `flatIndex` that can be 102// used to identify where the values for the given range begin 103// in the flattened arrays (e.g., `m_objects`) and descriptor 104// tables that hold the state of a shader object. 105// 106// * We also update the various counters taht keep track of the number 107// of sub-objects, resources, samplers, etc. that are being 108// consumed. These counters will contribute to figuring out 109// the descriptor table(s) that might be needed to represent 110// the object. 111// 112SlangInt bindingRangeCount = typeLayout -> getBindingRangeCount (); 113for (SlangInt r = 0 ;r < bindingRangeCount ;++ r ) 114 { 115 slang::BindingType slangBindingType = typeLayout -> getBindingRangeType (r ); 116uint32_t count = (uint32_t )typeLayout -> getBindingRangeBindingCount (r ); 117 slang::TypeLayoutReflection * slangLeafTypeLayout = 118typeLayout -> getBindingRangeLeafTypeLayout (r ); 119 120BindingRangeInfo bindingRangeInfo = {}; 121bindingRangeInfo .bindingType = slangBindingType ; 122bindingRangeInfo .resourceShape = slangLeafTypeLayout -> getResourceShape (); 123bindingRangeInfo .count = count ; 124bindingRangeInfo .isRootParameter = isBindingRangeRootParameter ( 125m_renderer -> slangContext .globalSession , 126static_cast < DeviceImpl *> (m_renderer )-> m_extendedDesc .rootParameterShaderAttributeName , 127typeLayout , 128r ); 129bindingRangeInfo .isSpecializable = typeLayout -> isBindingRangeSpecializable (r ); 130switch (slangBindingType ) 131 { 132case slang::BindingType ::RawBuffer : 133case slang::BindingType ::TypedBuffer : 134case slang::BindingType ::MutableRawBuffer : 135case slang::BindingType ::MutableTypedBuffer : 136 { 137auto bufferElementType = slangLeafTypeLayout -> getElementTypeLayout (); 138if (bufferElementType ) 139 { 140bindingRangeInfo .bufferElementStride = (uint32_t )bufferElementType -> getStride (); 141 } 142 } 143break ; 144 } 145if (bindingRangeInfo .isRootParameter ) 146 { 147RootParameterInfo rootInfo = {}; 148switch (slangBindingType ) 149 { 150case slang::BindingType ::RayTracingAccelerationStructure : 151rootInfo .type = IResourceView ::Type ::AccelerationStructure ; 152break ; 153case slang::BindingType ::RawBuffer : 154case slang::BindingType ::TypedBuffer : 155rootInfo .type = IResourceView ::Type ::ShaderResource ; 156break ; 157case slang::BindingType ::MutableRawBuffer : 158case slang::BindingType ::MutableTypedBuffer : 159rootInfo .type = IResourceView ::Type ::UnorderedAccess ; 160break ; 161 } 162bindingRangeInfo .baseIndex = (uint32_t )m_rootParamsInfo .getCount (); 163for (uint32_t i = 0 ;i < count ;i ++ ) 164 { 165m_rootParamsInfo .add (rootInfo ); 166 } 167 } 168else 169 { 170switch (slangBindingType ) 171 { 172case slang::BindingType ::ConstantBuffer : 173case slang::BindingType ::ParameterBlock : 174case slang::BindingType ::ExistentialValue : 175bindingRangeInfo .baseIndex = m_subObjectCount ; 176bindingRangeInfo .subObjectIndex = m_subObjectCount ; 177m_subObjectCount += count ; 178break ; 179case slang::BindingType ::RawBuffer : 180case slang::BindingType ::MutableRawBuffer : 181if (slangLeafTypeLayout -> getType ()-> getElementType ()!= nullptr ) 182 { 183// A structured buffer occupies both a resource slot and 184// a sub-object slot. 185bindingRangeInfo .subObjectIndex = m_subObjectCount ; 186m_subObjectCount += count ; 187 } 188bindingRangeInfo .baseIndex = m_ownCounts .resource ; 189m_ownCounts .resource += count ; 190break ; 191case slang::BindingType ::Sampler : 192bindingRangeInfo .baseIndex = m_ownCounts .sampler ; 193m_ownCounts .sampler += count ; 194break ; 195 196case slang::BindingType ::CombinedTextureSampler : 197// TODO: support this case... 198break ; 199 200case slang::BindingType ::VaryingInput : 201case slang::BindingType ::VaryingOutput : 202break ; 203 204default : 205bindingRangeInfo .baseIndex = m_ownCounts .resource ; 206m_ownCounts .resource += count ; 207break ; 208 } 209 } 210m_bindingRanges .add (bindingRangeInfo ); 211 } 212 213// At this point we've computed the number of resources/samplers that 214// the type needs to represent its *own* state, and stored those counts 215// in `m_ownCounts`. Next we need to consider any resources/samplers 216// and root parameters needed to represent the state of the transitive 217// sub-objects of this objet, so that we can compute the total size 218// of the object when bound to the pipeline. 219 220m_totalCounts = m_ownCounts ; 221 222SlangInt subObjectRangeCount = typeLayout -> getSubObjectRangeCount (); 223for (SlangInt r = 0 ;r < subObjectRangeCount ;++ r ) 224 { 225SlangInt bindingRangeIndex = typeLayout -> getSubObjectRangeBindingRangeIndex (r ); 226auto slangBindingType = typeLayout -> getBindingRangeType (bindingRangeIndex ); 227auto count = (uint32_t )typeLayout -> getBindingRangeBindingCount (bindingRangeIndex ); 228 slang::TypeLayoutReflection * slangLeafTypeLayout = 229typeLayout -> getBindingRangeLeafTypeLayout (bindingRangeIndex ); 230 231// A sub-object range can either represent a sub-object of a known 232// type, like a `ConstantBuffer<Foo>` or `ParameterBlock<Foo>` 233// (in which case we can pre-compute a layout to use, based on 234// the type `Foo`) *or* it can represent a sub-object of some 235// existential type (e.g., `IBar`) in which case we cannot 236// know the appropraite type/layout of sub-object to allocate. 237// 238RefPtr < ShaderObjectLayoutImpl > subObjectLayout ; 239if (slangBindingType == slang::BindingType ::ExistentialValue ) 240 { 241if (auto pendingTypeLayout = slangLeafTypeLayout -> getPendingDataTypeLayout ()) 242 { 243createForElementType ( 244m_renderer , 245m_session , 246pendingTypeLayout , 247subObjectLayout .writeRef ()); 248 } 249 } 250else 251 { 252createForElementType ( 253m_renderer , 254m_session , 255slangLeafTypeLayout -> getElementTypeLayout (), 256subObjectLayout .writeRef ()); 257 } 258 259SubObjectRangeInfo subObjectRange ; 260subObjectRange .bindingRangeIndex = bindingRangeIndex ; 261subObjectRange .layout = subObjectLayout ; 262 263// The Slang reflection API stors offset information for sub-object ranges, 264// and we care about *some* of that information: in particular, we need 265// the offset of sub-objects in terms of uniform/ordinary data for the 266// cases where we need to fill in "pending" data in our ordinary buffer. 267// 268subObjectRange .offset = SubObjectRangeOffset (typeLayout -> getSubObjectRangeOffset (r )); 269subObjectRange .stride = SubObjectRangeStride (slangLeafTypeLayout ); 270 271// The remaining offset information is computed based on the counters 272// we are generating here, which depend only on the in-memory layout 273// decisions being made in our implementation. Remember that the 274// `register` and `space` values coming from DXBC/DXIL do *not* 275// dictate the in-memory layout we use. 276// 277// Note: One subtle point here is that the `.rootParam` offset we are computing 278// here does *not* include any root parameters that would be allocated 279// for the parent object type itself (e.g., for descriptor tables 280// used if it were bound as a parameter block). The later logic when 281// we actually go to bind things will need to apply those offsets. 282// 283// Note: An even *more* subtle point is that the `.resource` offset 284// being computed here *does* include the resource descriptor allocated 285// for holding the ordinary data buffer, if any. The implications of 286// this for later offset math is subtle. 287// 288subObjectRange .offset .rootParam = m_childRootParameterCount ; 289subObjectRange .offset .resource = m_totalCounts .resource ; 290subObjectRange .offset .sampler = m_totalCounts .sampler ; 291 292// Along with the offset information, we also need to compute the 293// "stride" between consecutive sub-objects in the range. The actual 294// size/stride of a single object depends on the type of range we 295// are dealing with. 296// 297BindingOffset objectCounts ; 298switch (slangBindingType ) 299 { 300default : 301 { 302// We only treat buffers of interface types as actual sub-object binding 303// range. 304auto bindingRangeTypeLayout = 305typeLayout -> getBindingRangeLeafTypeLayout (bindingRangeIndex ); 306if (!bindingRangeTypeLayout ) 307continue ; 308auto elementType = typeLayout -> getBindingRangeLeafTypeLayout (bindingRangeIndex ) 309-> getElementTypeLayout (); 310if (!elementType ) 311continue ; 312if (elementType -> getKind ()!= slang::TypeReflection ::Kind ::Interface ) 313 { 314continue ; 315 } 316 } 317break ; 318 319case slang::BindingType ::ConstantBuffer : 320 { 321SLANG_ASSERT (subObjectLayout ); 322 323// The resource and sampler descriptors of a nested 324// constant buffer will "leak" into those of the 325// parent type, and we need to account for them 326// whenever we allocate storage. 327// 328objectCounts .resource = subObjectLayout -> getTotalResourceDescriptorCount (); 329objectCounts .sampler = subObjectLayout -> getTotalSamplerDescriptorCount (); 330objectCounts .rootParam = subObjectRange .layout -> getChildRootParameterCount (); 331 } 332break ; 333 334case slang::BindingType ::ParameterBlock : 335 { 336SLANG_ASSERT (subObjectLayout ); 337 338// In contrast to a constant buffer, a parameter block can hide 339// the resource and sampler descriptor allocation it uses (since they 340// are allocated into the tables that make up the parameter block. 341// 342// The only resource usage that leaks into the surrounding context 343// is the number of root parameters consumed. 344// 345objectCounts .rootParam = subObjectRange .layout -> getTotalRootTableParameterCount (); 346 } 347break ; 348 349case slang::BindingType ::ExistentialValue : 350// An unspecialized existential/interface value cannot consume any resources 351// as part of the parent object (it needs to fit inside the fixed-size 352// represnetation of existential types). 353// 354// However, if we are statically specializing to a type that doesn't "fit" 355// we may need to account for additional information that needs to be 356// allocaated. 357// 358if (subObjectLayout ) 359 { 360// The ordinary data for an existential-type value is allocated into 361// the same buffer as the parent object, so we only want to consider 362// the resource descriptors *other than* the ordinary data buffer. 363// 364// Otherwise the logic here is identical to the constant buffer case. 365// 366objectCounts .resource = 367subObjectLayout -> getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer (); 368objectCounts .sampler = subObjectLayout -> getTotalSamplerDescriptorCount (); 369objectCounts .rootParam = subObjectRange .layout -> getChildRootParameterCount (); 370 371// Note: In the implementation for some other graphics API (e.g., 372// Vulkan) there needs to be more work done to handle the fact that 373// "pending" data from interface-type sub-objects get allocated to a 374// distinct offset after all the "primary" data. We are consciously 375// ignoring that issue here, and the physical layout of a shader object 376// into the D3D12 binding state may end up interleaving 377// resources/samplers for "primary" and "pending" data. 378// 379// If this choice ever causes issues, we can revisit the approach here. 380 381// An interface-type range that includes ordinary data can 382// increase the size of the ordinary data buffer we need to 383// allocate for the parent object. 384// 385uint32_t ordinaryDataEnd = 386subObjectRange .offset .pendingOrdinaryData + 387 (uint32_t )count * subObjectRange .stride .pendingOrdinaryData ; 388 389if (ordinaryDataEnd > m_totalOrdinaryDataSize ) 390 { 391m_totalOrdinaryDataSize = ordinaryDataEnd ; 392 } 393 } 394break ; 395 } 396 397// Once we've computed the usage for each object in the range, we can 398// easily compute the usage for the entire range. 399// 400auto rangeResourceCount = count * objectCounts .resource ; 401auto rangeSamplerCount = count * objectCounts .sampler ; 402auto rangeRootParamCount = count * objectCounts .rootParam ; 403 404m_totalCounts .resource += rangeResourceCount ; 405m_totalCounts .sampler += rangeSamplerCount ; 406m_childRootParameterCount += rangeRootParamCount ; 407 408m_subObjectRanges .add (subObjectRange ); 409 } 410 411// Once we have added up the resource usage from all the sub-objects 412// we can look at the total number of resources and samplers that 413// need to be bound as part of this objects descriptor tables and 414// that will allow us to decide whether we need to allocate a root 415// parameter for a resource table or not, ans similarly for a 416// sampler table. 417// 418if (m_totalCounts .resource ) 419m_ownCounts .rootParam ++ ; 420if (m_totalCounts .sampler ) 421m_ownCounts .rootParam ++ ; 422 423m_totalCounts .rootParam = m_ownCounts .rootParam + m_childRootParameterCount ; 424 425return SLANG_OK ; 426} 427 428Result ShaderObjectLayoutImpl ::Builder ::build (ShaderObjectLayoutImpl ** outLayout ) 429{ 430auto layout = RefPtr < ShaderObjectLayoutImpl > (new ShaderObjectLayoutImpl ()); 431SLANG_RETURN_ON_FAIL (layout -> init (this )); 432 433returnRefPtrMove (outLayout ,layout ); 434return SLANG_OK ; 435} 436 437Result RootShaderObjectLayoutImpl ::Builder ::build (RootShaderObjectLayoutImpl ** outLayout ) 438{ 439RefPtr < RootShaderObjectLayoutImpl > layout = new RootShaderObjectLayoutImpl (); 440SLANG_RETURN_ON_FAIL (layout -> init (this )); 441 442returnRefPtrMove (outLayout ,layout ); 443return SLANG_OK ; 444} 445 446void RootShaderObjectLayoutImpl ::Builder ::addGlobalParams ( 447 slang::VariableLayoutReflection * globalsLayout ) 448{ 449setElementTypeLayout (globalsLayout -> getTypeLayout ()); 450} 451 452void RootShaderObjectLayoutImpl ::Builder ::addEntryPoint ( 453SlangStage stage , 454ShaderObjectLayoutImpl * entryPointLayout ) 455{ 456EntryPointInfo info ; 457info .layout = entryPointLayout ; 458 459info .offset .resource = m_totalCounts .resource ; 460info .offset .sampler = m_totalCounts .sampler ; 461info .offset .rootParam = m_childRootParameterCount ; 462 463m_totalCounts .resource += entryPointLayout -> getTotalResourceDescriptorCount (); 464m_totalCounts .sampler += entryPointLayout -> getTotalSamplerDescriptorCount (); 465 466// TODO(tfoley): Check this to make sure it is reasonable... 467m_childRootParameterCount += entryPointLayout -> getChildRootParameterCount (); 468 469m_entryPoints .add (info ); 470} 471 472Result RootShaderObjectLayoutImpl ::RootSignatureDescBuilder ::translateDescriptorRangeType ( 473 slang::BindingType c , 474D3D12_DESCRIPTOR_RANGE_TYPE * outType ) 475{ 476switch (c ) 477 { 478case slang::BindingType ::ConstantBuffer : 479* outType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV ; 480return SLANG_OK ; 481case slang::BindingType ::RawBuffer : 482case slang::BindingType ::Texture : 483case slang::BindingType ::TypedBuffer : 484case slang::BindingType ::RayTracingAccelerationStructure : 485* outType = D3D12_DESCRIPTOR_RANGE_TYPE_SRV ; 486return SLANG_OK ; 487case slang::BindingType ::MutableRawBuffer : 488case slang::BindingType ::MutableTexture : 489case slang::BindingType ::MutableTypedBuffer : 490* outType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV ; 491return SLANG_OK ; 492case slang::BindingType ::Sampler : 493* outType = D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER ; 494return SLANG_OK ; 495default : 496return SLANG_FAIL ; 497 } 498} 499 500/// Add a new descriptor set to the layout being computed. 501/// 502/// Note that a "descriptor set" in the layout may amount to 503/// zero, one, or two different descriptor *tables* in the 504/// final D3D12 root signature. Each descriptor set may 505/// contain zero or more view ranges (CBV/SRV/UAV) and zero 506/// or more sampler ranges. It maps to a view descriptor table 507/// if the number of view ranges is non-zero and to a sampler 508/// descriptor table if the number of sampler ranges is non-zero. 509/// 510 511uint32_t RootShaderObjectLayoutImpl ::RootSignatureDescBuilder ::addDescriptorSet () 512{ 513auto result = (uint32_t )m_descriptorSets .getCount (); 514m_descriptorSets .add (DescriptorSetLayout {}); 515return result ; 516} 517 518Result RootShaderObjectLayoutImpl ::RootSignatureDescBuilder ::addDescriptorRange ( 519Index physicalDescriptorSetIndex , 520D3D12_DESCRIPTOR_RANGE_TYPE rangeType , 521UINT registerIndex , 522UINT spaceIndex , 523UINT count , 524bool isRootParameter ) 525{ 526if (isRootParameter ) 527 { 528D3D12_ROOT_PARAMETER1 rootParam = {}; 529switch (rangeType ) 530 { 531case D3D12_DESCRIPTOR_RANGE_TYPE_SRV : 532rootParam .ParameterType = D3D12_ROOT_PARAMETER_TYPE_SRV ; 533break ; 534case D3D12_DESCRIPTOR_RANGE_TYPE_UAV : 535rootParam .ParameterType = D3D12_ROOT_PARAMETER_TYPE_UAV ; 536break ; 537default : 538getDebugCallback ()-> handleMessage ( 539DebugMessageType ::Error , 540DebugMessageSource ::Layer , 541"A shader parameter marked as root parameter is neither SRV nor UAV." ); 542return SLANG_FAIL ; 543 } 544rootParam .ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL ; 545rootParam .Descriptor .RegisterSpace = spaceIndex ; 546rootParam .Descriptor .ShaderRegister = registerIndex ; 547m_rootParameters .add (rootParam ); 548return SLANG_OK ; 549 } 550 551auto & descriptorSet = m_descriptorSets [physicalDescriptorSetIndex ]; 552 553D3D12_DESCRIPTOR_RANGE1 range = {}; 554range .RangeType = rangeType ; 555range .NumDescriptors = count ; 556range .BaseShaderRegister = registerIndex ; 557range .RegisterSpace = spaceIndex ; 558range .OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND ; 559 560if (range .RangeType == D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER ) 561 { 562descriptorSet .m_samplerRanges .add (range ); 563descriptorSet .m_samplerCount += range .NumDescriptors ; 564 } 565else 566 { 567descriptorSet .m_resourceRanges .add (range ); 568descriptorSet .m_resourceCount += range .NumDescriptors ; 569 } 570 571return SLANG_OK ; 572} 573 574/// Add one descriptor range as specified in Slang reflection information to the layout. 575/// 576/// The layout information is taken from `typeLayout` for the descriptor 577/// range with the given `descriptorRangeIndex` within the logical 578/// descriptor set (reflected by Slang) with the given `logicalDescriptorSetIndex`. 579/// 580/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of 581/// the descriptor set that the range should be added to. 582/// 583/// The `offset` encodes information about space and/or register offsets that 584/// should be applied to descrptor ranges. 585/// 586/// This operation can fail if the given descriptor range encodes a range that 587/// doesn't map to anything directly supported by D3D12. Higher-level routines 588/// will often want to ignore such failures. 589/// 590 591Result RootShaderObjectLayoutImpl ::RootSignatureDescBuilder ::addDescriptorRange ( 592 slang::TypeLayoutReflection * typeLayout , 593Index physicalDescriptorSetIndex , 594BindingRegisterOffset const & containerOffset , 595BindingRegisterOffset const & elementOffset , 596Index logicalDescriptorSetIndex , 597Index descriptorRangeIndex , 598bool isRootParameter ) 599{ 600auto bindingType = typeLayout -> getDescriptorSetDescriptorRangeType ( 601logicalDescriptorSetIndex , 602descriptorRangeIndex ); 603auto count = typeLayout -> getDescriptorSetDescriptorRangeDescriptorCount ( 604logicalDescriptorSetIndex , 605descriptorRangeIndex ); 606auto index = typeLayout -> getDescriptorSetDescriptorRangeIndexOffset ( 607logicalDescriptorSetIndex , 608descriptorRangeIndex ); 609auto space = typeLayout -> getDescriptorSetSpaceOffset (logicalDescriptorSetIndex ); 610 611D3D12_DESCRIPTOR_RANGE_TYPE rangeType ; 612SLANG_RETURN_ON_FAIL (translateDescriptorRangeType (bindingType ,& rangeType )); 613 614return addDescriptorRange ( 615physicalDescriptorSetIndex , 616rangeType , 617 (UINT )index + elementOffset [rangeType ], 618 (UINT )space + elementOffset .spaceOffset , 619 (UINT )count , 620isRootParameter ); 621} 622 623/// Add one binding range to the computed layout. 624/// 625/// The layout information is taken from `typeLayout` for the binding 626/// range with the given `bindingRangeIndex`. 627/// 628/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of 629/// the descriptor set that the range should be added to. 630/// 631/// The `offset` encodes information about space and/or register offsets that 632/// should be applied to descrptor ranges. 633/// 634/// Note that a single binding range may encompass zero or more descriptor ranges. 635/// 636 637void RootShaderObjectLayoutImpl ::RootSignatureDescBuilder ::addBindingRange ( 638 slang::TypeLayoutReflection * typeLayout , 639Index physicalDescriptorSetIndex , 640BindingRegisterOffset const & containerOffset , 641BindingRegisterOffset const & elementOffset , 642Index bindingRangeIndex ) 643{ 644auto logicalDescriptorSetIndex = 645typeLayout -> getBindingRangeDescriptorSetIndex (bindingRangeIndex ); 646auto firstDescriptorRangeIndex = 647typeLayout -> getBindingRangeFirstDescriptorRangeIndex (bindingRangeIndex ); 648Index descriptorRangeCount = typeLayout -> getBindingRangeDescriptorRangeCount (bindingRangeIndex ); 649bool isRootParameter = isBindingRangeRootParameter ( 650m_device -> slangContext .globalSession , 651m_device -> m_extendedDesc .rootParameterShaderAttributeName , 652typeLayout , 653bindingRangeIndex ); 654for (Index i = 0 ;i < descriptorRangeCount ;++ i ) 655 { 656auto descriptorRangeIndex = firstDescriptorRangeIndex + i ; 657 658// Note: we ignore the `Result` returned by `addDescriptorRange()` because we 659// want to silently skip any ranges that represent kinds of bindings that 660// don't actually exist in D3D12. 661// 662addDescriptorRange ( 663typeLayout , 664physicalDescriptorSetIndex , 665containerOffset , 666elementOffset , 667logicalDescriptorSetIndex , 668descriptorRangeIndex , 669isRootParameter ); 670 } 671} 672 673void RootShaderObjectLayoutImpl ::RootSignatureDescBuilder ::addAsValue ( 674 slang::VariableLayoutReflection * varLayout , 675Index physicalDescriptorSetIndex ) 676{ 677BindingRegisterOffsetPair offset (varLayout ); 678auto elementOffset = offset ; 679elementOffset .primary .spaceOffset = 0 ; 680elementOffset .pending .spaceOffset = 0 ; 681addAsValue (varLayout -> getTypeLayout (),physicalDescriptorSetIndex ,offset ,elementOffset ); 682} 683 684/// Add binding ranges and parameter blocks to the root signature. 685/// 686/// The layout information is taken from `typeLayout` which should 687/// be a layout for either a program or an entry point. 688/// 689/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of 690/// the descriptor set that binding ranges not belonging to nested 691/// parameter blocks should be added to. 692/// 693/// The `offsetForChildrenThatNeedNewSpace` and `offsetForOrdinaryChildren` parameters 694/// encode information about space and/or register offsets that should be applied to 695/// descrptor ranges. `offsetForChildrenThatNeedNewSpace` will contain a space offset 696/// for children that requires a new space, such as a ParameterBlock. 697/// `offsetForOrdinaryChildren` contains the space for all direct children that should 698/// be placed in. 699/// 700 701void RootShaderObjectLayoutImpl ::RootSignatureDescBuilder ::addAsConstantBuffer ( 702 slang::TypeLayoutReflection * typeLayout , 703Index physicalDescriptorSetIndex , 704BindingRegisterOffsetPair offsetForChildrenThatNeedNewSpace , 705BindingRegisterOffsetPair offsetForOrdinaryChildren ) 706{ 707if (typeLayout -> getSize (SLANG_PARAMETER_CATEGORY_UNIFORM )!= 0 ) 708 { 709auto descriptorRangeType = D3D12_DESCRIPTOR_RANGE_TYPE_CBV ; 710auto & offsetForRangeType = 711offsetForOrdinaryChildren .primary .offsetForRangeType [descriptorRangeType ]; 712addDescriptorRange ( 713physicalDescriptorSetIndex , 714descriptorRangeType , 715offsetForRangeType , 716offsetForOrdinaryChildren .primary .spaceOffset , 7171 , 718 false); 719offsetForRangeType ++ ; 720 } 721 722addAsValue ( 723typeLayout , 724physicalDescriptorSetIndex , 725offsetForChildrenThatNeedNewSpace , 726offsetForOrdinaryChildren ); 727} 728 729void RootShaderObjectLayoutImpl ::RootSignatureDescBuilder ::addAsValue ( 730 slang::TypeLayoutReflection * typeLayout , 731Index physicalDescriptorSetIndex , 732BindingRegisterOffsetPair containerOffset , 733BindingRegisterOffsetPair elementOffset ) 734{ 735// Our first task is to add the binding ranges for stuff that is 736// directly contained in `typeLayout` rather than via sub-objects. 737// 738// Our goal is to have the descriptors for directly-contained views/samplers 739// always be contiguous in CPU and GPU memory, so that we can write 740// to them easily with a single operaiton. 741// 742Index bindingRangeCount = typeLayout -> getBindingRangeCount (); 743for (Index bindingRangeIndex = 0 ;bindingRangeIndex < bindingRangeCount ;bindingRangeIndex ++ ) 744 { 745// We will look at the type of each binding range and intentionally 746// skip those that represent sub-objects. 747// 748auto bindingType = typeLayout -> getBindingRangeType (bindingRangeIndex ); 749switch (bindingType ) 750 { 751case slang::BindingType ::ConstantBuffer : 752case slang::BindingType ::ParameterBlock : 753case slang::BindingType ::ExistentialValue : 754continue ; 755 756default : 757break ; 758 } 759 760// For binding ranges that don't represent sub-objects, we will add 761// all of the descriptor ranges they encompass to the root signature. 762// 763addBindingRange ( 764typeLayout , 765physicalDescriptorSetIndex , 766containerOffset .primary , 767elementOffset .primary , 768bindingRangeIndex ); 769 } 770 771// Next we need to recursively include everything bound via sub-objects 772Index subObjectRangeCount = typeLayout -> getSubObjectRangeCount (); 773for (Index subObjectRangeIndex = 0 ;subObjectRangeIndex < subObjectRangeCount ; 774subObjectRangeIndex ++ ) 775 { 776auto bindingRangeIndex = 777typeLayout -> getSubObjectRangeBindingRangeIndex (subObjectRangeIndex ); 778auto bindingType = typeLayout -> getBindingRangeType (bindingRangeIndex ); 779 780auto subObjectTypeLayout = typeLayout -> getBindingRangeLeafTypeLayout (bindingRangeIndex ); 781 782BindingRegisterOffsetPair subObjectRangeContainerOffset = containerOffset ; 783subObjectRangeContainerOffset += 784BindingRegisterOffsetPair (typeLayout -> getSubObjectRangeOffset (subObjectRangeIndex )); 785BindingRegisterOffsetPair subObjectRangeElementOffset = elementOffset ; 786subObjectRangeElementOffset += 787BindingRegisterOffsetPair (typeLayout -> getSubObjectRangeOffset (subObjectRangeIndex )); 788subObjectRangeElementOffset .primary .spaceOffset = elementOffset .primary .spaceOffset ; 789subObjectRangeElementOffset .pending .spaceOffset = elementOffset .pending .spaceOffset ; 790 791switch (bindingType ) 792 { 793case slang::BindingType ::ConstantBuffer : 794 { 795auto containerVarLayout = subObjectTypeLayout -> getContainerVarLayout (); 796SLANG_ASSERT (containerVarLayout ); 797 798auto elementVarLayout = subObjectTypeLayout -> getElementVarLayout (); 799SLANG_ASSERT (elementVarLayout ); 800 801auto elementTypeLayout = elementVarLayout -> getTypeLayout (); 802SLANG_ASSERT (elementTypeLayout ); 803 804BindingRegisterOffsetPair containerOffset = subObjectRangeContainerOffset ; 805containerOffset += BindingRegisterOffsetPair (containerVarLayout ); 806 807BindingRegisterOffsetPair elementOffset = subObjectRangeElementOffset ; 808elementOffset += BindingRegisterOffsetPair (elementVarLayout ); 809 810addAsConstantBuffer ( 811elementTypeLayout , 812physicalDescriptorSetIndex , 813containerOffset , 814elementOffset ); 815 } 816break ; 817 818case slang::BindingType ::ParameterBlock : 819 { 820auto containerVarLayout = subObjectTypeLayout -> getContainerVarLayout (); 821SLANG_ASSERT (containerVarLayout ); 822 823auto elementVarLayout = subObjectTypeLayout -> getElementVarLayout (); 824SLANG_ASSERT (elementVarLayout ); 825 826auto elementTypeLayout = elementVarLayout -> getTypeLayout (); 827SLANG_ASSERT (elementTypeLayout ); 828 829BindingRegisterOffsetPair subDescriptorSetOffset ; 830subDescriptorSetOffset .primary .spaceOffset = 831subObjectRangeContainerOffset .primary .spaceOffset ; 832subDescriptorSetOffset .pending .spaceOffset = 833subObjectRangeContainerOffset .pending .spaceOffset ; 834 835auto subPhysicalDescriptorSetIndex = addDescriptorSet (); 836 837// We recursively call `addAsConstantBuffer` to actually generate 838// the root signature bindings for children in the parameter block. 839// We must compute `containerOffset`, which include a space offset 840// that any sub ParameterBlocks should start from, and `elementOffset` 841// that encodes the space offset of the current parameter block. 842// The space offset of the current parameter block can be obtained from the 843// `containerVarLayout`, and the space offset of any sub ParameterBlocks 844// are obatined from `elementVarLayout`. 845BindingRegisterOffsetPair offsetForChildrenThatNeedNewSpace = 846subDescriptorSetOffset ; 847offsetForChildrenThatNeedNewSpace += BindingRegisterOffsetPair (elementVarLayout ); 848BindingRegisterOffsetPair offsetForOrindaryChildren = subDescriptorSetOffset ; 849offsetForOrindaryChildren += BindingRegisterOffsetPair (containerVarLayout ); 850 851addAsConstantBuffer ( 852elementTypeLayout , 853subPhysicalDescriptorSetIndex , 854offsetForChildrenThatNeedNewSpace , 855offsetForOrindaryChildren ); 856 } 857break ; 858 859case slang::BindingType ::ExistentialValue : 860 { 861// Any nested binding ranges in the sub-object will "leak" into the 862// binding ranges for the surrounding context. 863// 864auto specializedTypeLayout = subObjectTypeLayout -> getPendingDataTypeLayout (); 865if (specializedTypeLayout ) 866 { 867BindingRegisterOffsetPair pendingOffset ; 868pendingOffset .primary = subObjectRangeElementOffset .pending ; 869 870addAsValue ( 871specializedTypeLayout , 872physicalDescriptorSetIndex , 873pendingOffset , 874pendingOffset ); 875 } 876 } 877break ; 878 } 879 } 880} 881 882D3D12_ROOT_SIGNATURE_DESC1 & RootShaderObjectLayoutImpl ::RootSignatureDescBuilder ::build () 883{ 884for (Index i = 0 ;i < m_descriptorSets .getCount ();i ++ ) 885 { 886auto & descriptorSet = m_descriptorSets [i ]; 887if (descriptorSet .m_resourceRanges .getCount ()) 888 { 889D3D12_ROOT_PARAMETER1 rootParam = {}; 890rootParam .ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE ; 891rootParam .DescriptorTable .NumDescriptorRanges = 892 (UINT )descriptorSet .m_resourceRanges .getCount (); 893rootParam .DescriptorTable .pDescriptorRanges = 894descriptorSet .m_resourceRanges .getBuffer (); 895m_rootParameters .add (rootParam ); 896 } 897if (descriptorSet .m_samplerRanges .getCount ()) 898 { 899D3D12_ROOT_PARAMETER1 rootParam = {}; 900rootParam .ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE ; 901rootParam .DescriptorTable .NumDescriptorRanges = 902 (UINT )descriptorSet .m_samplerRanges .getCount (); 903rootParam .DescriptorTable .pDescriptorRanges = descriptorSet .m_samplerRanges .getBuffer (); 904m_rootParameters .add (rootParam ); 905 } 906 } 907 908m_rootSignatureDesc .NumParameters = UINT (m_rootParameters .getCount ()); 909m_rootSignatureDesc .pParameters = m_rootParameters .getBuffer (); 910 911// TODO: static samplers should be reasonably easy to support... 912m_rootSignatureDesc .NumStaticSamplers = 0 ; 913m_rootSignatureDesc .pStaticSamplers = nullptr ; 914 915// TODO: only set this flag if needed (requires creating root 916// signature at same time as pipeline state...). 917// 918m_rootSignatureDesc .Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT ; 919 920return m_rootSignatureDesc ; 921} 922 923Result RootShaderObjectLayoutImpl ::createRootSignatureFromSlang ( 924DeviceImpl * device , 925RootShaderObjectLayoutImpl * rootLayout , 926 slang::IComponentType * program , 927ID3D12RootSignature ** outRootSignature , 928ID3DBlob ** outError ) 929{ 930// We are going to build up the root signature by adding 931// binding/descritpor ranges and nested parameter blocks 932// based on the computed layout information for `program`. 933// 934RootSignatureDescBuilder builder (device ); 935auto layout = program -> getLayout (); 936 937// The layout information computed by Slang breaks up shader 938// parameters into what we can think of as "logical" descriptor 939// sets based on whether or not parameters have the same `space`. 940// 941// We want to basically ignore that decomposition and generate a 942// single descriptor set to hold all top-level parameters, and only 943// generate distinct descriptor sets when the shader has opted in 944// via explicit parameter blocks. 945// 946// To achieve this goal, we will manually allocate a default descriptor 947// set for root parameters in our signature, and then recursively 948// add all the binding/descriptor ranges implied by the global-scope 949// parameters. 950// 951auto rootDescriptorSetIndex = builder .addDescriptorSet (); 952builder .addAsValue (layout -> getGlobalParamsVarLayout (),rootDescriptorSetIndex ); 953 954for (SlangUInt i = 0 ;i < layout -> getEntryPointCount ();i ++ ) 955 { 956// Entry-point parameters should also be added to the default root 957// descriptor set. 958// 959// We add the parameters using the "variable layout" for the entry point 960// and not just its type layout, to ensure that any offset information is 961// applied correctly to the `register` and `space` information for entry-point 962// parameters. 963// 964// Note: When we start to support DXR we will need to handle entry-point parameters 965// differently because they will need to map to local root signatures rather than 966// being included in the global root signature as is being done here. 967// 968auto entryPoint = layout -> getEntryPointByIndex (i ); 969builder .addAsValue (entryPoint -> getVarLayout (),rootDescriptorSetIndex ); 970 } 971 972auto & rootSignatureDesc = builder .build (); 973D3D12_VERSIONED_ROOT_SIGNATURE_DESC versionedDesc = {}; 974versionedDesc .Version = D3D_ROOT_SIGNATURE_VERSION_1_1 ; 975versionedDesc .Desc_1_1 = rootSignatureDesc ; 976ComPtr < ID3DBlob > signature ; 977ComPtr < ID3DBlob > error ; 978if (SLANG_FAILED (device -> m_D3D12SerializeVersionedRootSignature ( 979& versionedDesc , 980signature .writeRef (), 981error .writeRef ()))) 982 { 983getDebugCallback ()-> handleMessage ( 984DebugMessageType ::Error , 985DebugMessageSource ::Layer , 986"error: D3D12SerializeRootSignature failed" ); 987if (error ) 988 { 989getDebugCallback ()-> handleMessage ( 990DebugMessageType ::Error , 991DebugMessageSource ::Driver , 992 (const char * )error -> GetBufferPointer ()); 993if (outError ) 994returnComPtr (outError ,error ); 995 } 996return SLANG_FAIL ; 997 } 998 999SLANG_RETURN_ON_FAIL (device -> m_device -> CreateRootSignature ( 10000 , 1001signature -> GetBufferPointer (), 1002signature -> GetBufferSize (), 1003IID_PPV_ARGS (outRootSignature ))); 1004return SLANG_OK ; 1005} 1006 1007Result RootShaderObjectLayoutImpl ::create ( 1008DeviceImpl * device , 1009 slang::IComponentType * program , 1010 slang::ProgramLayout * programLayout , 1011RootShaderObjectLayoutImpl ** outLayout , 1012ID3DBlob ** outError ) 1013{ 1014RootShaderObjectLayoutImpl ::Builder builder (device ,program ,programLayout ); 1015builder .addGlobalParams (programLayout -> getGlobalParamsVarLayout ()); 1016 1017SlangInt entryPointCount = programLayout -> getEntryPointCount (); 1018for (SlangInt e = 0 ;e < entryPointCount ;++ e ) 1019 { 1020auto slangEntryPoint = programLayout -> getEntryPointByIndex (e ); 1021RefPtr < ShaderObjectLayoutImpl > entryPointLayout ; 1022SLANG_RETURN_ON_FAIL (ShaderObjectLayoutImpl ::createForElementType ( 1023device , 1024program -> getSession (), 1025slangEntryPoint -> getTypeLayout (), 1026entryPointLayout .writeRef ())); 1027builder .addEntryPoint (slangEntryPoint -> getStage (),entryPointLayout ); 1028 } 1029 1030RefPtr < RootShaderObjectLayoutImpl > layout ; 1031SLANG_RETURN_ON_FAIL (builder .build (layout .writeRef ())); 1032 1033if (program -> getSpecializationParamCount ()== 0 ) 1034 { 1035// For root object, we would like know the union of all binding slots 1036// including all sub-objects in the shader-object hierarchy, so at 1037// parameter binding time we can easily know how many GPU descriptor tables 1038// to create without walking through the shader-object hierarchy again. 1039// We build out this array along with root signature construction and store 1040// it in `m_gpuDescriptorSetInfos`. 1041SLANG_RETURN_ON_FAIL (createRootSignatureFromSlang ( 1042device , 1043layout , 1044program , 1045layout -> m_rootSignature .writeRef (), 1046outError )); 1047 } 1048 1049* outLayout = layout .detach (); 1050 1051return SLANG_OK ; 1052} 1053 1054Result RootShaderObjectLayoutImpl ::init (Builder * builder ) 1055{ 1056auto renderer = builder -> m_renderer ; 1057 1058SLANG_RETURN_ON_FAIL (Super ::init (builder )); 1059 1060m_program = builder -> m_program ; 1061m_programLayout = builder -> m_programLayout ; 1062m_entryPoints = builder -> m_entryPoints ; 1063return SLANG_OK ; 1064} 1065 1066}// namespace d3d12 1067}// namespace gfx