yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
e52885347
master
1// main.cpp 2 3// Reflection API Example Program 4// ============================== 5// 6// This file provides the application code for the `reflection-api` example. 7// This example uses the Slang reflection API to travserse the structure 8// of the parameters of a Slang program and their types. 9// 10// This program is a companion Slang reflection API documentation: 11// https://shader-slang.org/slang/user-guide/compiling.html 12// 13// Boilerplate 14// ----------- 15// 16// The following lines are boilerplate common to set up this example 17// to use the infrastructure for example programs in the Slang 18// repository. 19// 20 21#include "slang-com-ptr.h" 22#include "slang.h" 23typedef SlangResult Result ; 24 25#include "core/slang-basic.h" 26#include "examples/example-base/example-base.h" 27using Slang ::ComPtr ; 28using Slang ::String ; 29using Slang ::List ; 30 31static const ExampleResources resourceBase ("reflection-api" ); 32 33// Configuration 34// ------------- 35// 36// For simplicity, this example uses a hard-coded list of shader programs 37// to compile, each represented as the name of a `.slang` file, along with 38// a hard-coded list of targets to compile and reflect the programs for. 39// 40 41static const char * kSourceFileNames []= { 42"raster-simple.slang" , 43"compute-simple.slang" , 44}; 45 46static const struct 47{ 48SlangCompileTarget format ; 49const char * profile ; 50}kTargets []= { 51 {SLANG_DXIL ,"sm_6_0" }, 52 {SLANG_SPIRV ,"sm_6_0" }, 53}; 54static const int kTargetCount = SLANG_COUNT_OF (kTargets ); 55 56// The `ReflectingPrinting` Type 57// ------------------------- 58// 59// We wrap most of the code for this example in a `struct` 60// type, in order to provide a bit more freedom in order 61// of declaration. 62// 63// When possible, we will follow the order of declarations 64// in the accompanying document, to help readers who want 65// to following along in the code while reading. 66// 67struct ReflectingPrinting 68{ 69// Scoping things in a type allows us to declare functions 70// out of order more easily, but we still have to forward-declare 71// types when they will be used before they are declared. 72// 73struct AccessPath ; 74 75// Output Formatting 76// ----------------- 77// 78// This example program outputs reflection information in a format 79// that is (or at least is intended to be) compatible with YAML. 80// 81// We do not want the code to be overly complicated with issues 82// around formatting, so the details of the actual printing logic 83// are largely left until later. However, there are a pair of 84// macros that help to keep things tidy that we need to introduce 85// here, before they are used. 86// 87#define WITH_ARRAY () for (int _i = (beginArray(), 1); _i; _i = (endArray(), 0)) 88 89#define SCOPED_OBJECT () ScopedObject scopedObject##__COUNTER__(this) 90 91// Compiling a Program 92// ------------------- 93// 94Result compileAndReflectProgram (slang::ISession * session ,const char * sourceFileName ) 95 { 96SCOPED_OBJECT (); 97printComment ("program" ); 98 99key ("file name" ); 100printQuotedString (sourceFileName ); 101String sourceFilePath = resourceBase .resolveResource (sourceFileName ); 102 103ComPtr < slang::IBlob > diagnostics ; 104Result result = SLANG_OK ; 105 106// ### Loading a Module 107// 108 109ComPtr < slang::IModule > module ; 110module = session -> loadModule (sourceFilePath .getBuffer (),diagnostics .writeRef ()); 111diagnoseIfNeeded (diagnostics ); 112if (!module ) 113return SLANG_FAIL ; 114 115List < ComPtr < slang::IComponentType >> componentsToLink ; 116 117// ### Variable decls 118// 119key ("global constants" ); 120WITH_ARRAY () 121for (auto decl :module -> getModuleReflection ()-> getChildren ()) 122 { 123if (auto varDecl = decl -> asVariable ();varDecl && 124varDecl -> findModifier (slang::Modifier ::Const )&& 125varDecl -> findModifier (slang::Modifier ::Static )) 126 { 127element (); 128printVariable (varDecl ); 129 } 130 } 131 132// ### Finding Entry Points 133// 134 135key ("defined entry points" ); 136int definedEntryPointCount = module -> getDefinedEntryPointCount (); 137WITH_ARRAY () 138for (int i = 0 ;i < definedEntryPointCount ;i ++ ) 139 { 140ComPtr < slang::IEntryPoint > entryPoint ; 141SLANG_RETURN_ON_FAIL (module -> getDefinedEntryPoint (i ,entryPoint .writeRef ())); 142 143element (); 144SCOPED_OBJECT (); 145key ("name" ); 146printQuotedString (entryPoint -> getFunctionReflection ()-> getName ()); 147 148componentsToLink .add (ComPtr < slang::IComponentType > (entryPoint .get ())); 149 } 150 151// ### Composing and Linking 152// 153 154ComPtr < slang::IComponentType > composed ; 155result = session -> createCompositeComponentType ( 156 (slang::IComponentType ** )componentsToLink .getBuffer (), 157componentsToLink .getCount (), 158composed .writeRef (), 159diagnostics .writeRef ()); 160diagnoseIfNeeded (diagnostics ); 161SLANG_RETURN_ON_FAIL (result ); 162 163ComPtr < slang::IComponentType > program ; 164result = composed -> link (program .writeRef (),diagnostics .writeRef ()); 165diagnoseIfNeeded (diagnostics ); 166SLANG_RETURN_ON_FAIL (result ); 167 168key ("layouts" ); 169WITH_ARRAY () 170for (int targetIndex = 0 ;targetIndex < kTargetCount ;++ targetIndex ) 171 { 172element (); 173 174// ### Getting the Program Layout 175// 176 slang::ProgramLayout * programLayout = 177program -> getLayout (targetIndex ,diagnostics .writeRef ()); 178diagnoseIfNeeded (diagnostics ); 179if (!programLayout ) 180 { 181result = SLANG_FAIL ; 182continue ; 183 } 184 185SLANG_RETURN_ON_FAIL ( 186collectEntryPointMetadata (program ,targetIndex ,definedEntryPointCount )); 187 188_programLayout = programLayout ; 189auto targetFormat = kTargets [targetIndex ].format ; 190printProgramLayout (programLayout ,targetFormat ); 191 } 192 193return result ; 194 } 195 slang::ProgramLayout * _programLayout = nullptr ; 196 197Result compileAndReflectPrograms (slang::ISession * session ) 198 { 199Result result = SLANG_OK ; 200 201WITH_ARRAY () 202for (auto fileName :kSourceFileNames ) 203 { 204element (); 205auto programResult = compileAndReflectProgram (session ,fileName ); 206if (SLANG_FAILED (programResult )) 207 { 208result = programResult ; 209 } 210 } 211 212return result ; 213 } 214 215// Types and Variables 216// ------------------- 217// 218// ### Variables 219// 220void printVariable (slang::VariableReflection * variable ) 221 { 222SCOPED_OBJECT (); 223 224const char * name = variable -> getName (); 225 slang::TypeReflection * type = variable -> getType (); 226 227key ("name" ); 228printQuotedString (name ); 229key ("type" ); 230printType (type ); 231 232int64_t value ; 233if (SLANG_SUCCEEDED (variable -> getDefaultValueInt (& value ))) 234 { 235key ("value" ); 236printf ("%" PRId64 ,value ); 237 } 238 } 239 240// ### Types 241// 242void printType (slang::TypeReflection * type ) 243 { 244SCOPED_OBJECT (); 245 246const char * name = type -> getName (); 247 slang::TypeReflection ::Kind kind = type -> getKind (); 248 249key ("name" ); 250printQuotedString (name ); 251key ("kind" ); 252printTypeKind (kind ); 253 254// There is information that we would like to 255// print for both types and type layouts, so 256// we will factor the common logic into a 257// subroutine so that we can share the code. 258// 259printCommonTypeInfo (type ); 260 261switch (type -> getKind ()) 262 { 263default : 264break ; 265 266// #### Structure Types 267// 268case slang::TypeReflection ::Kind ::Struct : 269 { 270key ("fields" ); 271int fieldCount = type -> getFieldCount (); 272 273WITH_ARRAY (); 274for (int f = 0 ;f < fieldCount ;f ++ ) 275 { 276element (); 277auto field = type -> getFieldByIndex (f ); 278 279printVariable (field ); 280 } 281 } 282break ; 283 284// #### Array Types 285// #### Vector Types 286// #### Matrix Types 287// 288case slang::TypeReflection ::Kind ::Array : 289case slang::TypeReflection ::Kind ::Vector : 290case slang::TypeReflection ::Kind ::Matrix : 291 { 292key ("element type" ); 293printType (type -> getElementType ()); 294 } 295break ; 296 297// #### Resource Types 298// 299case slang::TypeReflection ::Kind ::Resource : 300 { 301key ("result type" ); 302printType (type -> getResourceResultType ()); 303 } 304break ; 305 306// #### Single-Element Container Types 307// 308case slang::TypeReflection ::Kind ::ConstantBuffer : 309case slang::TypeReflection ::Kind ::ParameterBlock : 310case slang::TypeReflection ::Kind ::TextureBuffer : 311case slang::TypeReflection ::Kind ::ShaderStorageBuffer : 312 { 313key ("element type" ); 314printType (type -> getElementType ()); 315 } 316break ; 317 } 318 } 319 320// #### Array Types 321// 322void printPossiblyUnbounded (size_t value ) 323 { 324if (value == ~size_t (0 )) 325 { 326printf ("unbounded" ); 327 } 328else 329 { 330printf ("%u" ,unsigned (value )); 331 } 332 } 333 334void printCommonTypeInfo (slang::TypeReflection * type ) 335 { 336switch (type -> getKind ()) 337 { 338// #### Scalar Types 339// 340case slang::TypeReflection ::Kind ::Scalar : 341 { 342key ("scalar type" ); 343printScalarType (type -> getScalarType ()); 344 } 345break ; 346 347// #### Array Types 348// 349case slang::TypeReflection ::Kind ::Array : 350 { 351key ("element count" ); 352printPossiblyUnbounded (type -> getElementCount ()); 353 } 354break ; 355 356// #### Vector Types 357// 358case slang::TypeReflection ::Kind ::Vector : 359 { 360key ("element count" ); 361type -> getElementCount ()); 362 } 363break ; 364 365// #### Matrix Types 366// 367case slang::TypeReflection ::Kind ::Matrix : 368 { 369key ("row count" ); 370type -> getRowCount ()); 371 372key ("column count" ); 373type -> getColumnCount ()); 374 } 375break ; 376 377// #### Resource Types 378// 379case slang::TypeReflection ::Kind ::Resource : 380 { 381key ("shape" ); 382printResourceShape (type -> getResourceShape ()); 383 384key ("access" ); 385printResourceAccess (type -> getResourceAccess ()); 386 } 387break ; 388 389default : 390break ; 391 } 392 } 393 394// Layout for Types and Variables 395// ------------------------------ 396// 397// ### Variable Layouts 398// 399void printVariableLayout (slang::VariableLayoutReflection * variableLayout ,AccessPath accessPath ) 400 { 401SCOPED_OBJECT (); 402 403key ("name" ); 404printQuotedString (variableLayout -> getName ()); 405 406printOffsets (variableLayout ,accessPath ); 407 408printVaryingParameterInfo (variableLayout ); 409 410ExtendedAccessPath variablePath (accessPath ,variableLayout ); 411 412key ("type layout" ); 413printTypeLayout (variableLayout -> getTypeLayout (),variablePath ); 414 } 415 416// #### Offsets 417 418void printRelativeOffsets (slang::VariableLayoutReflection * variableLayout ) 419 { 420key ("relative" ); 421int usedLayoutUnitCount = variableLayout -> getCategoryCount (); 422WITH_ARRAY (); 423for (int i = 0 ;i < usedLayoutUnitCount ;++ i ) 424 { 425element (); 426 427auto layoutUnit = variableLayout -> getCategoryByIndex (i ); 428printOffset (variableLayout ,layoutUnit ); 429 } 430 } 431 432void printOffset ( 433 slang::VariableLayoutReflection * variableLayout , 434 slang::ParameterCategory layoutUnit ) 435 { 436printOffset ( 437layoutUnit , 438variableLayout -> getOffset (layoutUnit ), 439variableLayout -> getBindingSpace (layoutUnit )); 440 } 441 442void printOffset (slang::ParameterCategory layoutUnit ,size_t offset ,size_t spaceOffset ) 443 { 444SCOPED_OBJECT (); 445 446key ("value" ); 447offset ); 448key ("unit" ); 449printLayoutUnit (layoutUnit ); 450 451// #### Spaces / Sets 452 453switch (layoutUnit ) 454 { 455default : 456break ; 457 458case slang::ParameterCategory ::ConstantBuffer : 459case slang::ParameterCategory ::ShaderResource : 460case slang::ParameterCategory ::UnorderedAccess : 461case slang::ParameterCategory ::SamplerState : 462case slang::ParameterCategory ::DescriptorTableSlot : 463key ("space" ); 464spaceOffset ); 465break ; 466 } 467 } 468 469// ### Type Layouts 470// 471void printTypeLayout (slang::TypeLayoutReflection * typeLayout ,AccessPath accessPath ) 472 { 473SCOPED_OBJECT (); 474 475key ("name" ); 476printQuotedString (typeLayout -> getName ()); 477key ("kind" ); 478printTypeKind (typeLayout -> getKind ()); 479printCommonTypeInfo (typeLayout -> getType ()); 480 481printSizes (typeLayout ); 482 483printKindSpecificInfo (typeLayout ,accessPath ); 484 } 485 486// #### Size 487// 488void printSizes (slang::TypeLayoutReflection * typeLayout ) 489 { 490key ("size" ); 491 492int usedLayoutUnitCount = typeLayout -> getCategoryCount (); 493WITH_ARRAY () 494for (int i = 0 ;i < usedLayoutUnitCount ;++ i ) 495 { 496element (); 497 498auto layoutUnit = typeLayout -> getCategoryByIndex (i ); 499printSize (typeLayout ,layoutUnit ); 500 } 501 502// #### Alignment and Stride 503if (typeLayout -> getSize ()!= 0 ) 504 { 505key ("alignment in bytes" ); 506typeLayout -> getAlignment ()); 507 508key ("stride in bytes" ); 509typeLayout -> getStride ()); 510 } 511 } 512 513void printSize (slang::TypeLayoutReflection * typeLayout , slang::ParameterCategory layoutUnit ) 514 { 515printSize (layoutUnit ,typeLayout -> getSize (layoutUnit )); 516 } 517 518void printSize (slang::ParameterCategory layoutUnit ,size_t size ) 519 { 520SCOPED_OBJECT (); 521 522key ("value" ); 523printPossiblyUnbounded (size ); 524key ("unit" ); 525printLayoutUnit (layoutUnit ); 526 } 527 528// #### Kind-Specific Information 529// 530void printKindSpecificInfo (slang::TypeLayoutReflection * typeLayout ,AccessPath accessPath ) 531 { 532switch (typeLayout -> getKind ()) 533 { 534// #### Structure Type Layouts 535// 536case slang::TypeReflection ::Kind ::Struct : 537 { 538key ("fields" ); 539 540int fieldCount = typeLayout -> getFieldCount (); 541WITH_ARRAY () 542for (int f = 0 ;f < fieldCount ;f ++ ) 543 { 544element (); 545 546auto field = typeLayout -> getFieldByIndex (f ); 547printVariableLayout (field ,accessPath ); 548 } 549 } 550break ; 551 552// #### Array Type Layouts 553// 554case slang::TypeReflection ::Kind ::Array : 555 { 556key ("element type layout" ); 557printTypeLayout (typeLayout -> getElementTypeLayout (),AccessPath ()); 558 } 559break ; 560 561// #### Matrix Type Layouts 562// 563case slang::TypeReflection ::Kind ::Matrix : 564 { 565key ("matrix layout mode" ); 566printMatrixLayoutMode (typeLayout -> getMatrixLayoutMode ()); 567 568key ("element type layout" ); 569printTypeLayout (typeLayout -> getElementTypeLayout (),AccessPath ()); 570 } 571break ; 572 573case slang::TypeReflection ::Kind ::Vector : 574 { 575key ("element type layout" ); 576printTypeLayout (typeLayout -> getElementTypeLayout (),AccessPath ()); 577 } 578break ; 579 580// #### Single-Element Containers 581// 582case slang::TypeReflection ::Kind ::ConstantBuffer : 583case slang::TypeReflection ::Kind ::ParameterBlock : 584case slang::TypeReflection ::Kind ::TextureBuffer : 585case slang::TypeReflection ::Kind ::ShaderStorageBuffer : 586 { 587auto containerVarLayout = typeLayout -> getContainerVarLayout (); 588auto elementVarLayout = typeLayout -> getElementVarLayout (); 589 590AccessPath innerOffsets = accessPath ; 591innerOffsets .deepestConstantBufer = innerOffsets .leaf ; 592if (containerVarLayout -> getTypeLayout ()-> getSize ( 593 slang::ParameterCategory ::SubElementRegisterSpace )!= 0 ) 594 { 595innerOffsets .deepestParameterBlock = innerOffsets .leaf ; 596 } 597 598key ("container" ); 599 { 600SCOPED_OBJECT (); 601printOffsets (containerVarLayout ,innerOffsets ); 602 } 603 604key ("content" ); 605 { 606SCOPED_OBJECT (); 607 608printOffsets (elementVarLayout ,innerOffsets ); 609 610ExtendedAccessPath elementOffsets (innerOffsets ,elementVarLayout ); 611 612key ("type layout" ); 613printTypeLayout (elementVarLayout -> getTypeLayout (),elementOffsets ); 614 } 615 } 616break ; 617 618case slang::TypeReflection ::Kind ::Resource : 619 { 620if ((typeLayout -> getResourceShape ()& SLANG_RESOURCE_BASE_SHAPE_MASK )== 621SLANG_STRUCTURED_BUFFER ) 622 { 623key ("element type layout" ); 624printTypeLayout (typeLayout -> getElementTypeLayout (),accessPath ); 625 } 626else 627 { 628key ("result type" ); 629printType (typeLayout -> getResourceResultType ()); 630 } 631 } 632break ; 633 634default : 635break ; 636 } 637 } 638 639// Programs and Scopes 640// ------------------- 641// 642void printProgramLayout (slang::ProgramLayout * programLayout ,SlangCompileTarget targetFormat ) 643 { 644SCOPED_OBJECT (); 645 646key ("target" ); 647printTargetFormat (targetFormat ); 648 649AccessPath rootOffsets ; 650rootOffsets .valid = true; 651 652key ("global scope" ); 653 { 654SCOPED_OBJECT (); 655printScope (programLayout -> getGlobalParamsVarLayout (),rootOffsets ); 656 } 657 658key ("entry points" ); 659int entryPointCount = programLayout -> getEntryPointCount (); 660WITH_ARRAY () 661for (int i = 0 ;i < entryPointCount ;++ i ) 662 { 663element (); 664printEntryPointLayout (programLayout -> getEntryPointByIndex (i ),rootOffsets ); 665 } 666 } 667 668// ### Global Scope 669// 670void printScope (slang::VariableLayoutReflection * scopeVarLayout ,AccessPath accessPath ) 671 { 672ExtendedAccessPath scopeOffsets (accessPath ,scopeVarLayout ); 673 674auto scopeTypeLayout = scopeVarLayout -> getTypeLayout (); 675switch (scopeTypeLayout -> getKind ()) 676 { 677// #### Parameters are Grouped Into a Structure 678// 679case slang::TypeReflection ::Kind ::Struct : 680 { 681key ("parameters" ); 682 683int paramCount = scopeTypeLayout -> getFieldCount (); 684for (int i = 0 ;i < paramCount ;i ++ ) 685 { 686element (); 687 688auto param = scopeTypeLayout -> getFieldByIndex (i ); 689 690printVariableLayout (param ,scopeOffsets ); 691 } 692 } 693break ; 694 695// #### Wrapped in a Constant Buffer If Needed 696// 697case slang::TypeReflection ::Kind ::ConstantBuffer : 698key ("automatically-introduced constant buffer" ); 699 { 700SCOPED_OBJECT (); 701printOffsets (scopeTypeLayout -> getContainerVarLayout (),scopeOffsets ); 702 } 703 704printScope (scopeTypeLayout -> getElementVarLayout (),scopeOffsets ); 705break ; 706 707// #### Wrapped in a Parameter Block If Needed 708// 709case slang::TypeReflection ::Kind ::ParameterBlock : 710key ("automatically-introduced parameter block" ); 711 { 712SCOPED_OBJECT (); 713printOffsets (scopeTypeLayout -> getContainerVarLayout (),scopeOffsets ); 714 } 715 716printScope (scopeTypeLayout -> getElementVarLayout (),scopeOffsets ); 717break ; 718 719default : 720// Note that this default case is never expected to 721// arise with the current Slang compiler and reflection 722// API, but we include it here as a kind of failsafe. 723// 724key ("variable layout" ); 725printVariableLayout (scopeVarLayout ,accessPath ); 726break ; 727 } 728 } 729 730// ### Entry Points 731// 732void printEntryPointLayout (slang::EntryPointReflection * entryPointLayout ,AccessPath accessPath ) 733 { 734SCOPED_OBJECT (); 735 736key ("stage" ); 737printStage (entryPointLayout -> getStage ()); 738 739printStageSpecificInfo (entryPointLayout ); 740 741printScope (entryPointLayout -> getVarLayout (),accessPath ); 742 743auto resultVariableLayout = entryPointLayout -> getResultVarLayout (); 744if (resultVariableLayout -> getTypeLayout ()-> getKind ()!= slang::TypeReflection ::Kind ::None ) 745 { 746key ("result" ); 747printVariableLayout (resultVariableLayout ,accessPath ); 748 } 749 } 750 751// #### Stage-Specific Information 752// 753void printStageSpecificInfo (slang::EntryPointReflection * entryPointLayout ) 754 { 755switch (entryPointLayout -> getStage ()) 756 { 757default : 758break ; 759 760case SLANG_STAGE_COMPUTE : 761 { 762static const int kAxisCount = 3 ; 763SlangUInt sizes [kAxisCount ]; 764entryPointLayout -> getComputeThreadGroupSize (kAxisCount ,sizes ); 765 766key ("thread group size" ); 767SCOPED_OBJECT (); 768key ("x" ); 769sizes [0 ]); 770key ("y" ); 771sizes [1 ]); 772key ("z" ); 773sizes [2 ]); 774 } 775break ; 776 777case SLANG_STAGE_FRAGMENT : 778key ("uses any sample-rate inputs" ); 779printBool (entryPointLayout -> usesAnySampleRateInput ()); 780break ; 781 } 782 } 783 784// #### Varying Parameters 785// 786void printVaryingParameterInfo (slang::VariableLayoutReflection * variableLayout ) 787 { 788if (auto semanticName = variableLayout -> getSemanticName ()) 789 { 790key ("semantic" ); 791SCOPED_OBJECT (); 792key ("name" ); 793printQuotedString (semanticName ); 794key ("index" ); 795variableLayout -> getSemanticIndex ()); 796 } 797 } 798 799// Calculating Cumulative Offsets 800// ------------------------------ 801// 802struct CumulativeOffset 803 { 804size_t value = 0 ; 805size_t space = 0 ; 806 }; 807 808// ### Access Paths 809 810struct AccessPathNode 811 { 812 slang::VariableLayoutReflection * variableLayout = nullptr ; 813AccessPathNode * outer = nullptr ; 814 }; 815 816struct AccessPath 817 { 818AccessPath () {} 819 820bool valid = false; 821AccessPathNode * deepestConstantBufer = nullptr ; 822AccessPathNode * deepestParameterBlock = nullptr ; 823AccessPathNode * leaf = nullptr ; 824 }; 825 826void printCumulativeOffsets ( 827 slang::VariableLayoutReflection * variableLayout , 828AccessPath accessPath ) 829 { 830key ("cumulative" ); 831 832int usedLayoutUnitCount = variableLayout -> getCategoryCount (); 833WITH_ARRAY (); 834for (int i = 0 ;i < usedLayoutUnitCount ;++ i ) 835 { 836element (); 837 838auto layoutUnit = variableLayout -> getCategoryByIndex (i ); 839printCumulativeOffset (variableLayout ,layoutUnit ,accessPath ); 840 } 841 } 842 843CumulativeOffset calculateCumulativeOffset ( 844 slang::VariableLayoutReflection * variableLayout , 845 slang::ParameterCategory layoutUnit , 846AccessPath accessPath ) 847 { 848CumulativeOffset result = calculateCumulativeOffset (layoutUnit ,accessPath ); 849result .value += variableLayout -> getOffset (layoutUnit ); 850result .space += variableLayout -> getBindingSpace (layoutUnit ); 851return result ; 852 } 853 854void printCumulativeOffset ( 855 slang::VariableLayoutReflection * variableLayout , 856 slang::ParameterCategory layoutUnit , 857AccessPath accessPath ) 858 { 859CumulativeOffset cumulativeOffset = 860calculateCumulativeOffset (variableLayout ,layoutUnit ,accessPath ); 861 862printOffset (layoutUnit ,cumulativeOffset .value ,cumulativeOffset .space ); 863 } 864 865// ### Tracking Access Paths 866 867struct ExtendedAccessPath :AccessPath 868 { 869ExtendedAccessPath (AccessPath const & base , slang::VariableLayoutReflection * variableLayout ) 870 :AccessPath (base ) 871 { 872if (!valid ) 873return ; 874 875element .variableLayout = variableLayout ; 876element .outer = leaf ; 877 878leaf = & element ; 879 } 880 881AccessPathNode element ; 882 }; 883 884// ### Accumulating Offsets Along An Access Path 885 886CumulativeOffset calculateCumulativeOffset ( 887 slang::ParameterCategory layoutUnit , 888AccessPath accessPath ) 889 { 890CumulativeOffset result ; 891switch (layoutUnit ) 892 { 893// #### Layout Units That Don't Require Special Handling 894// 895default : 896for (auto node = accessPath .leaf ;node != nullptr ;node = node -> outer ) 897 { 898result .value += node -> variableLayout -> getOffset (layoutUnit ); 899 } 900break ; 901 902// #### Bytes 903// 904case slang::ParameterCategory ::Uniform : 905for (auto node = accessPath .leaf ;node != accessPath .deepestConstantBufer ; 906node = node -> outer ) 907 { 908result .value += node -> variableLayout -> getOffset (layoutUnit ); 909 } 910break ; 911 912// #### Layout Units That Care About Spaces 913// 914case slang::ParameterCategory ::ConstantBuffer : 915case slang::ParameterCategory ::ShaderResource : 916case slang::ParameterCategory ::UnorderedAccess : 917case slang::ParameterCategory ::SamplerState : 918case slang::ParameterCategory ::DescriptorTableSlot : 919for (auto node = accessPath .leaf ;node != accessPath .deepestParameterBlock ; 920node = node -> outer ) 921 { 922result .value += node -> variableLayout -> getOffset (layoutUnit ); 923result .space += node -> variableLayout -> getBindingSpace (layoutUnit ); 924 } 925for (auto node = accessPath .deepestParameterBlock ;node != nullptr ;node = node -> outer ) 926 { 927result .space += node -> variableLayout -> getOffset ( 928 slang::ParameterCategory ::SubElementRegisterSpace ); 929 } 930break ; 931 } 932return result ; 933 } 934 935// Determining Whether Parameters Are Used 936// --------------------------------------- 937 938Result collectEntryPointMetadata ( 939 slang::IComponentType * program , 940int targetIndex , 941int entryPointCount ) 942 { 943_metadataForEntryPoints .setCount (entryPointCount ); 944for (int entryPointIndex = 0 ;entryPointIndex < entryPointCount ;entryPointIndex ++ ) 945 { 946ComPtr < slang::IMetadata > entryPointMetadata ; 947ComPtr < slang::IBlob > diagnostics ; 948SLANG_RETURN_ON_FAIL (program -> getEntryPointMetadata ( 949entryPointIndex , 950targetIndex , 951entryPointMetadata .writeRef (), 952diagnostics .writeRef ())); 953diagnoseIfNeeded (diagnostics ); 954 955_metadataForEntryPoints [entryPointIndex ]= entryPointMetadata ; 956 } 957return SLANG_OK ; 958 } 959Slang ::List < ComPtr < slang::IMetadata >> _metadataForEntryPoints ; 960 961typedef unsigned int StageMask ; 962 963StageMask calculateParameterStageMask ( 964 slang::ParameterCategory layoutUnit , 965CumulativeOffset offset ) 966 { 967unsigned mask = 0 ; 968auto entryPointCount = _metadataForEntryPoints .getCount (); 969for (int i = 0 ;i < entryPointCount ;++ i ) 970 { 971bool isUsed = false; 972_metadataForEntryPoints [i ]-> isParameterLocationUsed ( 973SlangParameterCategory (layoutUnit ), 974offset .space , 975offset .value , 976isUsed ); 977if (isUsed ) 978 { 979auto entryPointStage = _programLayout -> getEntryPointByIndex (i )-> getStage (); 980 981mask |=1 <<unsigned (entryPointStage ); 982 } 983 } 984return mask ; 985 } 986 987StageMask calculateStageMask ( 988 slang::VariableLayoutReflection * variableLayout , 989AccessPath accessPath ) 990 { 991StageMask mask = 0 ; 992 993int usedLayoutUnitCount = variableLayout -> getCategoryCount (); 994for (int i = 0 ;i < usedLayoutUnitCount ;++ i ) 995 { 996auto layoutUnit = variableLayout -> getCategoryByIndex (i ); 997auto offset = calculateCumulativeOffset (variableLayout ,layoutUnit ,accessPath ); 998 999mask |=calculateParameterStageMask (layoutUnit ,offset ); 1000 } 1001 1002return mask ; 1003 } 1004 1005void printStageUsage (slang::VariableLayoutReflection * variableLayout ,AccessPath accessPath ) 1006 { 1007StageMask stageMask = calculateStageMask (variableLayout ,accessPath ); 1008 1009key ("used by stages" ); 1010WITH_ARRAY () 1011for (int i = 0 ;i < SLANG_STAGE_COUNT ;i ++ ) 1012 { 1013if (stageMask & (1 <<i )) 1014 { 1015element (); 1016printStage (SlangStage (i )); 1017 } 1018 } 1019 } 1020 1021void printOffsets (slang::VariableLayoutReflection * variableLayout ,AccessPath accessPath ) 1022 { 1023key ("offset" ); 1024 { 1025SCOPED_OBJECT (); 1026printRelativeOffsets (variableLayout ); 1027 1028if (accessPath .valid ) 1029 { 1030printCumulativeOffsets (variableLayout ,accessPath ); 1031 } 1032 } 1033 1034 1035if (accessPath .valid ) 1036 { 1037printStageUsage (variableLayout ,accessPath ); 1038 } 1039 } 1040 1041// Formatting 1042// ---------- 1043// 1044// Here we'll cover the logic for how we implement 1045// the various formatting operations used in the 1046// code above. 1047// 1048// ### Indentation 1049// 1050// We track a global indentation level, and whenever 1051// we begin a new line, we'll emit a corresponding 1052// amount of space (two spaces per indent, consistent 1053// with typical YAML formatting). 1054 1055int indentation = 0 ; 1056 1057void printIndentation () 1058 { 1059for (int i = 1 ;i < indentation ;++ i ) 1060 { 1061printf (" " ); 1062 } 1063 } 1064 1065// ### Objects and Arrays 1066// 1067// Both objects and arrays can be marked up purely 1068// with indentation in YAML. If we eventually 1069// change the output format to something like JSON, 1070// these operations would need to do more actual 1071// work. 1072 1073void beginObject () {indentation ++ ; } 1074 1075void endObject () {indentation -- ; } 1076 1077void beginArray () {indentation ++ ; } 1078 1079void endArray () {indentation -- ; } 1080 1081// #### Scope-Based Objects 1082// 1083// In order to make it easier to keep the `beginObject()` 1084// and `endObject()` calls properly paired, we introduce 1085// a helper type that uses an RAII idiom to automatically 1086// pair up the calls. 1087// 1088struct ScopedObject 1089 { 1090ScopedObject (ReflectingPrinting * outer ) 1091 :outer (outer ) 1092 { 1093outer -> beginObject (); 1094 } 1095 1096 ~ScopedObject () {outer -> endObject (); } 1097 1098ReflectingPrinting * outer = nullptr ; 1099 }; 1100 1101// ### Starting New Lines 1102// 1103// Typically, when we are about to emit a key 1104// in an object, or an element in an array, 1105// we need to start a new line (and print 1106// the appropriate indentation). 1107// 1108void newLine () 1109 { 1110printf ("\n" ); 1111printIndentation (); 1112 } 1113 1114 1115// The main exception is that if we've just 1116// emitted the `- ` for an array element then 1117// we don't need to start a new line if 1118// the next thing we emit is an object key. 1119// 1120// We *also* don't need to start a new line 1121// at the very beginning of the output, so 1122// we handle that by setting the intial state 1123// *as if* we have just started an array element. 1124 1125bool afterArrayElement = true; 1126 1127// ### Array Elements 1128// 1129void element () 1130 { 1131newLine (); 1132printf ("- " ); 1133afterArrayElement = true; 1134 } 1135 1136// ### Object Keys 1137// 1138void key (char const * key ) 1139 { 1140if (!afterArrayElement ) 1141 { 1142newLine (); 1143 } 1144afterArrayElement = false; 1145 1146printf ("%s: " ,key ); 1147 } 1148 1149// ### Printing Simple Values 1150// 1151// Simple scalar values like strings, 1152// `bool`s, and numbers don't need 1153// much special handling. 1154 1155void printQuotedString (char const * text ) 1156 { 1157if (text ) 1158 { 1159printf ("\"%s\"" ,text ); 1160 } 1161else 1162 { 1163printf ("null" ); 1164 } 1165 } 1166 1167void printBool (bool value ) {printf (value ?"true" :"false" ); } 1168 1169void size_t value ) {printf ("%u" ,unsigned (value )); } 1170 1171// YAML supports comments, but JSON doesn't. 1172// This function could be stubbed out if 1173// we switch up the output format. 1174// 1175void printComment (char const * text ) {printf ("# %s" ,text ); } 1176 1177 1178// Printing Enumerants 1179// ------------------- 1180// 1181// Here we'll gather all the logic for printing the various 1182// `enum` types that we've worked with in the logic above. 1183 1184void printTypeKind (slang::TypeReflection ::Kind kind ) 1185 { 1186switch (kind ) 1187 { 1188#define CASE (TAG ) \ 1189 case slang::TypeReflection::Kind::TAG: \ 1190 printf("%s", #TAG); \ 1191 break 1192 1193CASE (None ); 1194CASE (Struct ); 1195CASE (Array ); 1196CASE (Matrix ); 1197CASE (Vector ); 1198CASE (Scalar ); 1199CASE (ConstantBuffer ); 1200CASE (Resource ); 1201CASE (SamplerState ); 1202CASE (TextureBuffer ); 1203CASE (ShaderStorageBuffer ); 1204CASE (ParameterBlock ); 1205CASE (GenericTypeParameter ); 1206CASE (Interface ); 1207CASE (OutputStream ); 1208CASE (Specialized ); 1209CASE (Feedback ); 1210CASE (Pointer ); 1211CASE (DynamicResource ); 1212#undef CASE 1213 1214default : 1215printf ("%d # unexpected enumerant" ,int (kind )); 1216break ; 1217 } 1218 } 1219 1220void printResourceShape (SlangResourceShape shape ) 1221 { 1222SCOPED_OBJECT (); 1223 1224key ("base" ); 1225auto baseShape = shape & SLANG_RESOURCE_BASE_SHAPE_MASK ; 1226switch (baseShape ) 1227 { 1228#define CASE (TAG ) \ 1229 case SLANG_##TAG: \ 1230 printf("%s", #TAG); \ 1231 break 1232 1233CASE (TEXTURE_1D ); 1234CASE (TEXTURE_2D ); 1235CASE (TEXTURE_3D ); 1236CASE (TEXTURE_CUBE ); 1237CASE (TEXTURE_BUFFER ); 1238CASE (STRUCTURED_BUFFER ); 1239CASE (BYTE_ADDRESS_BUFFER ); 1240CASE (RESOURCE_UNKNOWN ); 1241CASE (ACCELERATION_STRUCTURE ); 1242CASE (TEXTURE_SUBPASS ); 1243#undef CASE 1244 1245default : 1246printf ("%d # unexpected enumerant" ,int (baseShape )); 1247break ; 1248 } 1249 1250#define CASE (TAG ) \ 1251 do \ 1252 { \ 1253 if (shape & SLANG_TEXTURE_##TAG##_FLAG) \ 1254 { \ 1255 key(#TAG); \ 1256 printf("true"); \ 1257 } \ 1258 } while (0) 1259 1260CASE (FEEDBACK ); 1261CASE (SHADOW ); 1262CASE (ARRAY ); 1263CASE (MULTISAMPLE ); 1264#undef CASE 1265 } 1266 1267void printResourceAccess (SlangResourceAccess access ) 1268 { 1269switch (access ) 1270 { 1271#define CASE (TAG ) \ 1272 case SLANG_RESOURCE_ACCESS_##TAG: \ 1273 printf("%s", #TAG); \ 1274 break 1275 1276CASE (NONE ); 1277CASE (READ ); 1278CASE (READ_WRITE ); 1279CASE (RASTER_ORDERED ); 1280CASE (APPEND ); 1281CASE (CONSUME ); 1282CASE (WRITE ); 1283CASE (FEEDBACK ); 1284#undef CASE 1285 1286default : 1287printf ("%d # unexpected enumerant" ,int (access )); 1288break ; 1289 } 1290 } 1291 1292void printLayoutUnit (slang::ParameterCategory layoutUnit ) 1293 { 1294switch (layoutUnit ) 1295 { 1296#define CASE (TAG ,DESCRIPTION ) \ 1297 case slang::ParameterCategory::TAG: \ 1298 printf("%s # %s", #TAG, DESCRIPTION); \ 1299 break 1300 1301CASE (ConstantBuffer ,"constant buffer slots" ); 1302CASE (ShaderResource ,"texture slots" ); 1303CASE (UnorderedAccess ,"uav slots" ); 1304CASE (VaryingInput ,"varying input slots" ); 1305CASE (VaryingOutput ,"varying output slots" ); 1306CASE (SamplerState ,"sampler slots" ); 1307CASE (Uniform ,"bytes" ); 1308CASE (DescriptorTableSlot ,"bindings" ); 1309CASE (SpecializationConstant ,"specialization constant ids" ); 1310CASE (PushConstantBuffer ,"push-constant buffers" ); 1311CASE (RegisterSpace ,"register space offset for a variable" ); 1312CASE (GenericResource ,"generic resources" ); 1313CASE (RayPayload ,"ray payloads" ); 1314CASE (HitAttributes ,"hit attributes" ); 1315CASE (CallablePayload ,"callable payloads" ); 1316CASE (ShaderRecord ,"shader records" ); 1317CASE (ExistentialTypeParam ,"existential type parameters" ); 1318CASE (ExistentialObjectParam ,"existential object parameters" ); 1319CASE (SubElementRegisterSpace ,"register spaces / descriptor sets" ); 1320CASE (InputAttachmentIndex ,"subpass input attachments" ); 1321CASE (MetalArgumentBufferElement ,"Metal argument buffer elements" ); 1322CASE (MetalAttribute ,"Metal attributes" ); 1323CASE (MetalPayload ,"Metal payloads" ); 1324#undef CASE 1325 1326default : 1327printf ("%d # unknown enumerant" ,int (layoutUnit )); 1328break ; 1329 } 1330 } 1331 1332void printStage (SlangStage stage ) 1333 { 1334switch (stage ) 1335 { 1336#define CASE (NAME ) \ 1337 case SLANG_STAGE_##NAME: \ 1338 printf(#NAME); \ 1339 break 1340 1341CASE (NONE ); 1342CASE (VERTEX ); 1343CASE (HULL ); 1344CASE (DOMAIN ); 1345CASE (GEOMETRY ); 1346CASE (FRAGMENT ); 1347CASE (COMPUTE ); 1348CASE (RAY_GENERATION ); 1349CASE (INTERSECTION ); 1350CASE (ANY_HIT ); 1351CASE (CLOSEST_HIT ); 1352CASE (MISS ); 1353CASE (CALLABLE ); 1354CASE (MESH ); 1355CASE (AMPLIFICATION ); 1356#undef CASE 1357 1358default : 1359printf ("%d # unexpected enumerant" ,int (stage )); 1360break ; 1361 }; 1362 } 1363void printTargetFormat (SlangCompileTarget targetFormat ) 1364 { 1365switch (targetFormat ) 1366 { 1367#define CASE (TAG ) \ 1368 case SLANG_##TAG: \ 1369 printf("%s", #TAG); \ 1370 break 1371 1372CASE (TARGET_UNKNOWN ); 1373CASE (TARGET_NONE ); 1374CASE (GLSL ); 1375CASE (GLSL_VULKAN_DEPRECATED ); 1376CASE (GLSL_VULKAN_ONE_DESC_DEPRECATED ); 1377CASE (HLSL ); 1378CASE (SPIRV ); 1379CASE (SPIRV_ASM ); 1380CASE (DXBC ); 1381CASE (DXBC_ASM ); 1382CASE (DXIL ); 1383CASE (DXIL_ASM ); 1384CASE (C_SOURCE ); 1385CASE (CPP_SOURCE ); 1386CASE (HOST_EXECUTABLE ); 1387CASE (SHADER_SHARED_LIBRARY ); 1388CASE (SHADER_HOST_CALLABLE ); 1389CASE (CUDA_SOURCE ); 1390CASE (PTX ); 1391CASE (CUDA_OBJECT_CODE ); 1392CASE (OBJECT_CODE ); 1393CASE (HOST_CPP_SOURCE ); 1394CASE (HOST_HOST_CALLABLE ); 1395CASE (CPP_PYTORCH_BINDING ); 1396CASE (METAL ); 1397CASE (METAL_LIB ); 1398CASE (METAL_LIB_ASM ); 1399CASE (HOST_SHARED_LIBRARY ); 1400CASE (WGSL ); 1401CASE (WGSL_SPIRV_ASM ); 1402CASE (WGSL_SPIRV ); 1403#undef CASE 1404 1405default : 1406printf ("%d # unhandled enumerant" ,int (targetFormat )); 1407 } 1408 } 1409 1410void printScalarType (slang::TypeReflection ::ScalarType scalarType ) 1411 { 1412switch (scalarType ) 1413 { 1414#define CASE (TAG ) \ 1415 case slang::TypeReflection::TAG: \ 1416 printf("%s", #TAG); \ 1417 break 1418 1419CASE (None ); 1420CASE (Void ); 1421CASE (Bool ); 1422CASE (Int32 ); 1423CASE (UInt32 ); 1424CASE (Int64 ); 1425CASE (UInt64 ); 1426CASE (Float16 ); 1427CASE (Float32 ); 1428CASE (Float64 ); 1429CASE (Int8 ); 1430CASE (UInt8 ); 1431CASE (Int16 ); 1432CASE (UInt16 ); 1433#undef CASE 1434 1435default : 1436printf ("%d # unhandled enumerant" ,int (scalarType )); 1437 } 1438 } 1439 1440void printMatrixLayoutMode (SlangMatrixLayoutMode mode ) 1441 { 1442switch (mode ) 1443 { 1444#define CASE (TAG ) \ 1445 case SLANG_MATRIX_LAYOUT_##TAG: \ 1446 printf("%s", #TAG); \ 1447 break 1448 1449CASE (MODE_UNKNOWN ); 1450CASE (ROW_MAJOR ); 1451CASE (COLUMN_MAJOR ); 1452#undef CASE 1453 1454default : 1455printf ("%d # unhandled enumerant" ,int (mode )); 1456 } 1457 } 1458}; 1459 1460struct ExampleProgram :public TestBase 1461{ 1462Result execute (int argc ,char * argv []) 1463 { 1464parseOption (argc ,argv ); 1465 1466ComPtr < slang::IGlobalSession > globalSession ; 1467SLANG_RETURN_ON_FAIL (slang::createGlobalSession (globalSession .writeRef ())); 1468 1469Slang ::List < slang::TargetDesc > targetDescs ; 1470for (auto target :kTargets ) 1471 { 1472auto profile = globalSession -> findProfile (target .profile ); 1473 1474 slang::TargetDesc targetDesc ; 1475targetDesc .format = target .format ; 1476targetDesc .profile = profile ; 1477targetDescs .add (targetDesc ); 1478 } 1479 1480 slang::SessionDesc sessionDesc ; 1481sessionDesc .targetCount = targetDescs .getCount (); 1482sessionDesc .targets = targetDescs .getBuffer (); 1483 1484ComPtr < slang::ISession > session ; 1485SLANG_RETURN_ON_FAIL (globalSession -> createSession (sessionDesc ,session .writeRef ())); 1486 1487ReflectingPrinting printingContext ; 1488printingContext .compileAndReflectPrograms (session ); 1489 1490return SLANG_OK ; 1491 } 1492}; 1493 1494int exampleMain (int argc ,char ** argv ) 1495{ 1496ExampleProgram app ; 1497if (SLANG_FAILED (app .execute (argc ,argv ))) 1498 { 1499return -1 ; 1500 } 1501return 0 ; 1502}