yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
f65d756bf
master
1// d3d12-shader-object-layout.h 2#pragma once 3 4#include "d3d12-base.h" 5 6namespace gfx 7{ 8namespace d3d12 9{ 10 11using namespace Slang ; 12 13/// A representation of the offset at which to bind a shader parameter or sub-object 14struct BindingOffset 15{ 16// Note: When we actually bind a shader object to the pipeline we do not care about 17// HLSL-specific notions like `t` registers and `space`s. Those concepts are all 18// mediated by the root signature. 19// 20// Instead, we need to consider the offsets at which the object will be bound 21// into the actual D3D12 API state, which consists of the index of the current 22// root parameter to bind from, as well as indices into the current descriptor 23// tables (for resource views and samplers). 24 25uint32_t rootParam = 0 ; 26uint32_t resource = 0 ; 27uint32_t sampler = 0 ; 28 29void operator += (BindingOffset const & offset ) 30 { 31rootParam += offset .rootParam ; 32resource += offset .resource ; 33sampler += offset .sampler ; 34 } 35}; 36 37// Provides information on how binding ranges are stored in descriptor tables for 38// a shader object. 39// We allocate one CPU descriptor table for each descriptor heap type for the shader 40// object. In `ShaderObjectLayoutImpl`, we store the offset into the descriptor tables 41// for each binding, so we know where to write the descriptor when the user sets 42// a resource or sampler binding. 43class ShaderObjectLayoutImpl :public ShaderObjectLayoutBase 44{ 45public : 46/// Information about a single logical binding range 47struct BindingRangeInfo 48 { 49// Some of the information we store on binding ranges is redundant with 50// the information that Slang's reflection information stores, but having 51// it here can make the code more compact and obvious. 52 53/// The type of binding in this range. 54slang ::BindingType bindingType ; 55 56/// The shape of the resource 57SlangResourceShape resourceShape ; 58 59/// The number of distinct bindings in this range. 60uint32_t count ; 61 62/// A "flat" index for this range in whatever array provides backing storage for it 63uint32_t baseIndex ; 64 65/// An index into the sub-object array if this binding range is treated 66/// as a sub-object. 67uint32_t subObjectIndex ; 68 69/// The stride of a structured buffer. 70uint32_t bufferElementStride ; 71 72bool isRootParameter ; 73 74/// Is this binding range represent a specialization point, such as an existential value, or 75/// a `ParameterBlock<IFoo>`. 76bool isSpecializable ; 77 }; 78 79/// Offset information for a sub-object range 80struct SubObjectRangeOffset : BindingOffset 81{ 82SubObjectRangeOffset () {} 83 84SubObjectRangeOffset ( slang :: VariableLayoutReflection * varLayout); 85 86/// The offset for "pending" ordinary data related to this range 87uint32_t pendingOrdinaryData = 0 ; 88}; 89 90/// Stride information for a sub-object range 91struct SubObjectRangeStride : BindingOffset 92{ 93SubObjectRangeStride () {} 94 95SubObjectRangeStride ( slang :: TypeLayoutReflection * typeLayout); 96 97/// The strid for "pending" ordinary data related to this range 98uint32_t pendingOrdinaryData = 0 ; 99}; 100 101/// Information about a sub-objecrt range 102struct SubObjectRangeInfo 103{ 104/// The index of the binding range corresponding to this sub-object range 105Index bindingRangeIndex = 0 ; 106 107/// Layout information for the type of sub-object expected to be bound, if known 108RefPtr < ShaderObjectLayoutImpl > layout ; 109 110/// The offset to use when binding the first object in this range 111SubObjectRangeOffset offset ; 112 113/// Stride between consecutive objects in this range 114SubObjectRangeStride stride ; 115}; 116 117struct RootParameterInfo 118{ 119IResourceView :: Type type ; 120}; 121 122static bool isBindingRangeRootParameter ( 123SlangSession * globalSession, 124const char * rootParameterAttributeName, 125slang ::TypeLayoutReflection * typeLayout, 126Index bindingRangeIndex); 127 128struct Builder 129{ 130public : 131Builder ( RendererBase * renderer, slang ::ISession * session) 132: m_renderer (renderer), m_session ( session ) 133{ 134} 135 136RendererBase * m_renderer ; 137slang :: ISession * m_session ; 138slang :: TypeLayoutReflection * m_elementTypeLayout ; 139List < BindingRangeInfo > m_bindingRanges ; 140List < SubObjectRangeInfo > m_subObjectRanges ; 141List < RootParameterInfo > m_rootParamsInfo ; 142 143/// The number of sub-objects (not just sub-object *ranges*) stored in instances of this 144/// layout 145uint32_t m_subObjectCount = 0 ; 146 147/// Counters for the number of root parameters, resources, and samplers in this object 148/// itself 149BindingOffset m_ownCounts ; 150 151/// Counters for the number of root parameters, resources, and sampler in this object 152/// and transitive sub-objects 153BindingOffset m_totalCounts ; 154 155/// The number of root parameter consumed by (transitive) sub-objects 156uint32_t m_childRootParameterCount = 0 ; 157 158/// The total size in bytes of the ordinary data for this object and transitive 159/// sub-object. 160uint32_t m_totalOrdinaryDataSize = 0 ; 161 162/// The container type of this shader object. When `m_containerType` is 163/// `StructuredBuffer` or `UnsizedArray`, this shader object represents a collection 164/// instead of a single object. 165ShaderObjectContainerType m_containerType = ShaderObjectContainerType ::None; 166 167Result setElementTypeLayout ( slang ::TypeLayoutReflection * typeLayout); 168 169Result build ( ShaderObjectLayoutImpl ** outLayout); 170}; 171 172static Result createForElementType ( 173RendererBase * renderer, 174slang ::ISession * session, 175slang ::TypeLayoutReflection * elementType, 176ShaderObjectLayoutImpl ** outLayout); 177 178List < BindingRangeInfo > const & getBindingRanges () { return m_bindingRanges; } 179 180Index getBindingRangeCount () { return m_bindingRanges. getCount (); } 181 182BindingRangeInfo const & getBindingRange ( Index index) { return m_bindingRanges[index]; } 183 184uint32_t getResourceSlotCount () { return m_ownCounts. resource ; } 185uint32_t getSamplerSlotCount () { return m_ownCounts. sampler ; } 186Index getSubObjectSlotCount () { return m_subObjectCount; } 187Index getSubObjectCount () { return m_subObjectCount; } 188 189uint32_t getTotalResourceDescriptorCount () { return m_totalCounts. resource ; } 190uint32_t getTotalSamplerDescriptorCount () { return m_totalCounts. sampler ; } 191 192uint32_t getOrdinaryDataBufferCount () { return m_totalOrdinaryDataSize ? 1 : 0 ; } 193bool hasOrdinaryDataBuffer () { return m_totalOrdinaryDataSize != 0 ; } 194 195uint32_t getTotalResourceDescriptorCountWithoutOrdinaryDataBuffer () 196{ 197return m_totalCounts. resource - getOrdinaryDataBufferCount (); 198} 199 200uint32_t getOwnUserRootParameterCount () { return ( uint32_t )m_rootParamsInfo. getCount (); } 201uint32_t getTotalRootTableParameterCount () { return m_totalCounts. rootParam ; } 202uint32_t getChildRootParameterCount () { return m_childRootParameterCount; } 203 204uint32_t getTotalOrdinaryDataSize () const { return m_totalOrdinaryDataSize; } 205 206SubObjectRangeInfo const & getSubObjectRange ( Index index) { return m_subObjectRanges[index]; } 207List < SubObjectRangeInfo > const & getSubObjectRanges () { return m_subObjectRanges; } 208 209RendererBase * getRenderer () { return m_renderer; } 210 211slang :: TypeReflection * getType () { return m_elementTypeLayout -> getType (); } 212 213const RootParameterInfo & getRootParameterInfo ( Index index) { return m_rootParamsInfo[index]; } 214 215protected : 216Result init ( Builder * builder); 217 218List < BindingRangeInfo > m_bindingRanges; 219List < SubObjectRangeInfo > m_subObjectRanges; 220List < RootParameterInfo > m_rootParamsInfo; 221 222BindingOffset m_ownCounts; 223BindingOffset m_totalCounts; 224 225uint32_t m_subObjectCount = 0 ; 226uint32_t m_childRootParameterCount = 0 ; 227 228uint32_t m_totalOrdinaryDataSize = 0 ; 229}; 230 231class RootShaderObjectLayoutImpl : public ShaderObjectLayoutImpl 232{ 233typedef ShaderObjectLayoutImpl Super ; 234 235public : 236struct EntryPointInfo 237{ 238RefPtr < ShaderObjectLayoutImpl > layout ; 239BindingOffset offset ; 240}; 241 242struct Builder : Super::Builder 243{ 244Builder ( 245RendererBase * renderer, 246slang::IComponentType * program, 247slang::ProgramLayout * programLayout) 248: Super:: Builder (renderer, program -> getSession ()) 249, m_program (program) 250, m_programLayout (programLayout) 251{ 252} 253 254Result build ( RootShaderObjectLayoutImpl ** outLayout); 255 256void addGlobalParams ( slang ::VariableLayoutReflection * globalsLayout); 257 258void addEntryPoint ( SlangStage stage, ShaderObjectLayoutImpl * entryPointLayout); 259 260slang :: IComponentType * m_program; 261slang :: ProgramLayout * m_programLayout; 262List < EntryPointInfo > m_entryPoints; 263}; 264 265EntryPointInfo & getEntryPoint (Index index) { return m_entryPoints[index]; } 266 267List < EntryPointInfo >& getEntryPoints () { return m_entryPoints; } 268 269struct DescriptorSetLayout 270{ 271List < D3D12_DESCRIPTOR_RANGE1 > m_resourceRanges ; 272List < D3D12_DESCRIPTOR_RANGE1 > m_samplerRanges ; 273uint32_t m_resourceCount = 0 ; 274uint32_t m_samplerCount = 0 ; 275}; 276 277struct RootSignatureDescBuilder 278{ 279DeviceImpl * m_device ; 280 281RootSignatureDescBuilder( DeviceImpl * device) 282: m_device ( device ) 283{ 284} 285 286// We will use one descriptor set for the global scope and one additional 287// descriptor set for each `ParameterBlock` binding range in the shader object 288// hierarchy, regardless of the shader's `space` indices. 289List < DescriptorSetLayout > m_descriptorSets; 290List < D3D12_ROOT_PARAMETER1 > m_rootParameters; 291List < D3D12_ROOT_PARAMETER1 > m_rootDescTableParameters; 292 293D3D12_ROOT_SIGNATURE_DESC1 m_rootSignatureDesc = {}; 294 295static Result translateDescriptorRangeType ( 296slang ::BindingType c, 297D3D12_DESCRIPTOR_RANGE_TYPE * outType); 298 299/// Stores offset information to apply to the reflected register/space for a descriptor 300/// range. 301/// 302struct BindingRegisterOffset 303{ 304uint32_t spaceOffset = 0 ; // The `space` index as specified in shader. 305 306enum 307{ 308kRangeTypeCount = 4 309}; 310 311/// An offset to apply for each D3D12 register class, as given 312/// by a `D3D12_DESCRIPTOR_RANGE_TYPE`. 313/// 314/// Note that the `D3D12_DESCRIPTOR_RANGE_TYPE` enumeration has 315/// values between 0 and 3, inclusive. 316/// 317uint32_t offsetForRangeType [kRangeTypeCount] = { 0 , 0 , 0 , 0 }; 318 319uint32_t & operator []( D3D12_DESCRIPTOR_RANGE_TYPE type) 320{ 321return offsetForRangeType [ int (type)]; 322} 323 324uint32_t operator[]( D3D12_DESCRIPTOR_RANGE_TYPE type) const 325{ 326return offsetForRangeType[ int (type)]; 327} 328 329BindingRegisterOffset () {} 330 331BindingRegisterOffset ( slang :: VariableLayoutReflection * varLayout) 332{ 333if (varLayout) 334{ 335spaceOffset = ( UINT )varLayout -> getOffset ( 336SLANG_PARAMETER_CATEGORY_SUB_ELEMENT_REGISTER_SPACE ); 337offsetForRangeType[ D3D12_DESCRIPTOR_RANGE_TYPE_CBV ] = 338( UINT )varLayout -> getOffset ( SLANG_PARAMETER_CATEGORY_CONSTANT_BUFFER ); 339offsetForRangeType[ D3D12_DESCRIPTOR_RANGE_TYPE_SRV ] = 340( UINT )varLayout -> getOffset ( SLANG_PARAMETER_CATEGORY_SHADER_RESOURCE ); 341offsetForRangeType[ D3D12_DESCRIPTOR_RANGE_TYPE_UAV ] = 342( UINT )varLayout -> getOffset ( SLANG_PARAMETER_CATEGORY_UNORDERED_ACCESS ); 343offsetForRangeType[ D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER ] = 344( UINT )varLayout -> getOffset ( SLANG_PARAMETER_CATEGORY_SAMPLER_STATE ); 345} 346} 347 348void operator += (BindingRegisterOffset const & other) 349{ 350spaceOffset += other. spaceOffset ; 351for ( int i = 0 ; i < kRangeTypeCount; ++ i) 352{ 353offsetForRangeType[i] += other. offsetForRangeType [i]; 354} 355} 356}; 357 358struct BindingRegisterOffsetPair 359{ 360BindingRegisterOffset primary ; 361BindingRegisterOffset pending ; 362 363BindingRegisterOffsetPair() {} 364 365BindingRegisterOffsetPair( slang :: VariableLayoutReflection * varLayout ) 366: primary ( varLayout ), pending ( varLayout -> getPendingDataLayout ()) 367{ 368} 369 370void operator += ( BindingRegisterOffsetPair const & other ) 371{ 372primary += other . primary ; 373pending += other . pending ; 374} 375}; 376/// Add a new descriptor set to the layout being computed. 377/// 378/// Note that a "descriptor set" in the layout may amount to 379/// zero, one, or two different descriptor *tables* in the 380/// final D3D12 root signature. Each descriptor set may 381/// contain zero or more view ranges (CBV/SRV/UAV) and zero 382/// or more sampler ranges. It maps to a view descriptor table 383/// if the number of view ranges is non-zero and to a sampler 384/// descriptor table if the number of sampler ranges is non-zero. 385/// 386uint32_t addDescriptorSet (); 387 388Result addDescriptorRange ( 389Index physicalDescriptorSetIndex, 390D3D12_DESCRIPTOR_RANGE_TYPE rangeType, 391UINT registerIndex, 392UINT spaceIndex, 393UINT count, 394bool isRootParameter); 395/// Add one descriptor range as specified in Slang reflection information to the layout. 396/// 397/// The layout information is taken from `typeLayout` for the descriptor 398/// range with the given `descriptorRangeIndex` within the logical 399/// descriptor set (reflected by Slang) with the given `logicalDescriptorSetIndex`. 400/// 401/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of 402/// the descriptor set that the range should be added to. 403/// 404/// The `offset` encodes information about space and/or register offsets that 405/// should be applied to descrptor ranges. 406/// 407/// This operation can fail if the given descriptor range encodes a range that 408/// doesn't map to anything directly supported by D3D12. Higher-level routines 409/// will often want to ignore such failures. 410/// 411Result addDescriptorRange ( 412slang ::TypeLayoutReflection * typeLayout, 413Index physicalDescriptorSetIndex, 414BindingRegisterOffset const & containerOffset, 415BindingRegisterOffset const & elementOffset, 416Index logicalDescriptorSetIndex, 417Index descriptorRangeIndex, 418bool isRootParameter); 419 420/// Add one binding range to the computed layout. 421/// 422/// The layout information is taken from `typeLayout` for the binding 423/// range with the given `bindingRangeIndex`. 424/// 425/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of 426/// the descriptor set that the range should be added to. 427/// 428/// The `offset` encodes information about space and/or register offsets that 429/// should be applied to descrptor ranges. 430/// 431/// Note that a single binding range may encompass zero or more descriptor ranges. 432/// 433void addBindingRange ( 434slang ::TypeLayoutReflection * typeLayout, 435Index physicalDescriptorSetIndex, 436BindingRegisterOffset const & containerOffset, 437BindingRegisterOffset const & elementOffset, 438Index bindingRangeIndex); 439 440void addAsValue ( 441slang ::VariableLayoutReflection * varLayout, 442Index physicalDescriptorSetIndex); 443 444/// Add binding ranges and parameter blocks to the root signature. 445/// 446/// The layout information is taken from `typeLayout` which should 447/// be a layout for either a program or an entry point. 448/// 449/// The `physicalDescriptorSetIndex` is the index in the `m_descriptorSets` array of 450/// the descriptor set that binding ranges not belonging to nested 451/// parameter blocks should be added to. 452/// 453/// The `offset` encodes information about space and/or register offsets that 454/// should be applied to descrptor ranges. 455/// 456void addAsConstantBuffer ( 457slang ::TypeLayoutReflection * typeLayout, 458Index physicalDescriptorSetIndex, 459BindingRegisterOffsetPair containerOffset, 460BindingRegisterOffsetPair elementOffset); 461 462void addAsValue ( 463slang ::TypeLayoutReflection * typeLayout, 464Index physicalDescriptorSetIndex, 465BindingRegisterOffsetPair containerOffset, 466BindingRegisterOffsetPair elementOffset); 467 468D3D12_ROOT_SIGNATURE_DESC1 & build (); 469}; 470 471static Result createRootSignatureFromSlang ( 472DeviceImpl * device, 473RootShaderObjectLayoutImpl * rootLayout, 474slang ::IComponentType * program, 475ID3D12RootSignature ** outRootSignature, 476ID3DBlob ** outError); 477 478static Result create ( 479DeviceImpl * device, 480slang ::IComponentType * program, 481slang ::ProgramLayout * programLayout, 482RootShaderObjectLayoutImpl ** outLayout, 483ID3DBlob ** outError); 484 485slang :: IComponentType * getSlangProgram () const { return m_program; } 486slang :: ProgramLayout * getSlangProgramLayout () const { return m_programLayout; } 487 488protected : 489Result init ( Builder * builder); 490 491ComPtr < slang::IComponentType > m_program; 492slang :: ProgramLayout * m_programLayout = nullptr ; 493 494List < EntryPointInfo > m_entryPoints; 495 496public : 497ComPtr < ID3D12RootSignature > m_rootSignature; 498}; 499 500} // namespace d3d12 501} // namespace gfx