yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
23dcea810
master
1// slang-support.cpp 2 3#define _CRT_SECURE_NO_WARNINGS 1 4 5#include "slang-support.h" 6 7#include "../../source/compiler-core/slang-artifact-desc-util.h" 8#include "../../source/core/slang-file-system.h" 9#include "../../source/core/slang-string-util.h" 10#include "../../source/core/slang-test-tool-util.h" 11#include "options.h" 12 13#include <assert.h> 14#include <stdio.h> 15 16namespace renderer_test 17{ 18using namespace Slang ; 19 20// Entry point name to use for vertex/fragment shader 21static const char vertexEntryPointName []= "vertexMain" ; 22static const char fragmentEntryPointName []= "fragmentMain" ; 23static const char computeEntryPointName []= "computeMain" ; 24static const char rtEntryPointName []= "raygenMain" ; 25static const char taskEntryPointName []= "taskMain" ; 26static const char meshEntryPointName []= "meshMain" ; 27 28void ShaderCompilerUtil ::Output ::set (slang::IComponentType * inSlangProgram ) 29{ 30slangProgram = inSlangProgram ; 31desc .slangGlobalScope = inSlangProgram ; 32} 33 34void ShaderCompilerUtil ::Output ::reset () 35{ 36 { 37desc .slangGlobalScope = nullptr ; 38 } 39 40globalSession = nullptr ; 41m_session = nullptr ; 42} 43 44static SlangResult _compileProgramImpl ( 45 slang::IGlobalSession * globalSession , 46const Options & options , 47const ShaderCompilerUtil ::Input & input , 48const ShaderCompileRequest & request , 49ShaderCompilerUtil ::Output & out ) 50{ 51out .reset (); 52 53List < const char *> args ; 54for (const auto & arg :options .downstreamArgs .getArgsByName ("slang" )) 55 { 56args .add (arg .value .getBuffer ()); 57// The -load-repro feature is not maintained, and not supported by the new compile API. 58// TODO: Remove this when the feature has been deprecated. 59SLANG_ASSERT (arg .value != "-load-repro" ); 60 } 61 62 slang::TargetDesc sessionTargetDesc = {}; 63 slang::SessionDesc sessionDesc = {}; 64ComPtr < ISlangUnknown > sessionDescMemory ; 65// If there are additional args parse them 66if (args .getCount ()) 67 { 68const auto res = globalSession -> parseCommandLineArguments ( 69int (args .getCount ()), 70args .getBuffer (), 71& sessionDesc , 72sessionDescMemory .writeRef ()); 73// If there is a parse failure and diagnostic, output it 74if (SLANG_FAILED (res )) 75 { 76fprintf (stderr ,"error: Failed to parse command line arguments: %d\n" ,int (res )); 77return res ; 78 } 79// We're setting the targets ourselves, below. 80// To simplify that, we're currently not expecting targets to be added by the command line 81// arguments. 82if (sessionDesc .targetCount > 0 ) 83 { 84fprintf (stderr ,"error: Command line arguments added targets.\n" ); 85return SLANG_FAIL ; 86 } 87 } 88 89// Argument parsing may have already added options, so add those first. 90// For module reference options there are two cases: 91// 1. If it's a slang module, then record the path and later create an IModule from that. 92// 2. If not, then propagate the option. 93// The reason to propagate the option in case 2 is that there is not currently a way of 94// representing a module for a downstream compiler in the compilation API. 95List < slang::CompilerOptionEntry > sessionOptionEntries ; 96List < Slang ::String > referencedSlangModulePaths ; 97for (int optionIndex = 0 ;optionIndex < sessionDesc .compilerOptionEntryCount ;optionIndex ++ ) 98 { 99 slang::CompilerOptionEntry & option = sessionDesc .compilerOptionEntries [optionIndex ]; 100if (option .name == slang::CompilerOptionName ::ReferenceModule ) 101 { 102SLANG_ASSERT (option .value .kind == slang::CompilerOptionValueKind ::String ); 103const char * path = option .value .stringValue0 ; 104auto desc = Slang ::ArtifactDescUtil ::getDescFromPath (Slang ::UnownedStringSlice (path )); 105switch (desc .payload ) 106 { 107case Slang ::ArtifactDesc ::Payload ::SlangIR : 108case Slang ::ArtifactDesc ::Payload ::Slang : 109referencedSlangModulePaths .add (option .value .stringValue0 ); 110break ; 111case Slang ::ArtifactDesc ::Payload ::DXIL : 112sessionOptionEntries .add (option ); 113break ; 114default : 115 { 116fprintf ( 117stderr , 118"error: Unexpected artifact payload type: %d\n" , 119 (int )desc .payload ); 120return SLANG_FAIL ; 121 } 122 } 123 } 124else 125 { 126sessionOptionEntries .add (option ); 127 } 128 } 129 130List < slang::PreprocessorMacroDesc > macros ; 131 132// Define a macro so that shader code in a test can detect what language we 133// are nominally working with. 134char const * langDefine = nullptr ; 135switch (input .sourceLanguage ) 136 { 137case SLANG_SOURCE_LANGUAGE_GLSL : 138macros .add ({"__GLSL__" ,"1" }); 139break ; 140 141case SLANG_SOURCE_LANGUAGE_SLANG : 142macros .add ({"__SLANG__" ,"1" }); 143// fall through 144case SLANG_SOURCE_LANGUAGE_HLSL : 145macros .add ({"__HLSL__" ,"1" }); 146break ; 147case SLANG_SOURCE_LANGUAGE_C : 148macros .add ({"__C__" ,"1" }); 149break ; 150case SLANG_SOURCE_LANGUAGE_CPP : 151macros .add ({"__CPP__" ,"1" }); 152break ; 153case SLANG_SOURCE_LANGUAGE_CUDA : 154macros .add ({"__CUDA__" ,"1" }); 155break ; 156case SLANG_SOURCE_LANGUAGE_WGSL : 157macros .add ({"__WGSL__" ,"1" }); 158break ; 159 160default : 161assert (!"unexpected" ); 162break ; 163 } 164 165 { 166 slang::CompilerOptionEntry entry ; 167entry .name = slang::CompilerOptionName ::AllowGLSL ; 168entry .value .kind = slang::CompilerOptionValueKind ::Int ; 169entry .value .intValue0 = int (options .allowGLSL ); 170sessionOptionEntries .add (entry ); 171 } 172 173 { 174 slang::CompilerOptionEntry entry ; 175entry .name = slang::CompilerOptionName ::PassThrough ; 176entry .value .kind = slang::CompilerOptionValueKind ::Int ; 177entry .value .intValue0 = int (input .passThrough ); 178sessionOptionEntries .add (entry ); 179 } 180 181 { 182 slang::CompilerOptionEntry entry ; 183entry .name = slang::CompilerOptionName ::LineDirectiveMode ; 184entry .value .kind = slang::CompilerOptionValueKind ::Int ; 185entry .value .intValue0 = int (SlangLineDirectiveMode ::SLANG_LINE_DIRECTIVE_MODE_NONE ); 186sessionOptionEntries .add (entry ); 187 } 188 189sessionTargetDesc .format = input .target ; 190if (input .profile .getLength ())// do not set profile unless requested 191sessionTargetDesc .profile = globalSession -> findProfile (input .profile .getBuffer ()); 192if (options .generateSPIRVDirectly ) 193sessionTargetDesc .flags |=SLANG_TARGET_FLAG_GENERATE_SPIRV_DIRECTLY ; 194else 195sessionTargetDesc .flags = 0 ; 196 197 { 198 slang::CompilerOptionEntry entry ; 199entry .value .kind = slang::CompilerOptionValueKind ::Int ; 200if (options .generateSPIRVDirectly ) 201 { 202entry .name = slang::CompilerOptionName ::EmitSpirvDirectly ; 203entry .value .intValue0 = int (options .generateSPIRVDirectly ); 204 } 205else 206 { 207entry .name = slang::CompilerOptionName ::EmitSpirvViaGLSL ; 208entry .value .intValue0 = int (!options .generateSPIRVDirectly ); 209 } 210sessionOptionEntries .add (entry ); 211 } 212 213// Not expecting argument parsing to have added any targets 214SLANG_ASSERT (sessionDesc .targetCount == 0 ); 215sessionDesc .targetCount = 1 ; 216sessionDesc .targets = & sessionTargetDesc ; 217 218sessionDesc .skipSPIRVValidation = options .skipSPIRVValidation ; 219if (options .generateSPIRVDirectly ) 220 { 221 slang::CompilerOptionEntry entry ; 222entry .name = slang::CompilerOptionName ::DebugInformation ; 223entry .value .kind = slang::CompilerOptionValueKind ::Int ; 224entry .value .intValue0 = 225int (options .disableDebugInfo ?SlangDebugInfoLevel ::SLANG_DEBUG_INFO_LEVEL_NONE 226 :SlangDebugInfoLevel ::SLANG_DEBUG_INFO_LEVEL_STANDARD ); 227sessionOptionEntries .add (entry ); 228 } 229 230for (auto & capability :options .capabilities ) 231 { 232 slang::CompilerOptionEntry entry ; 233entry .name = slang::CompilerOptionName ::Capability ; 234entry .value .kind = slang::CompilerOptionValueKind ::String ; 235entry .value .stringValue0 = capability .getBuffer (); 236sessionOptionEntries .add (entry ); 237 } 238 239sessionDesc .compilerOptionEntryCount = sessionOptionEntries .getCount (); 240sessionDesc .compilerOptionEntries = sessionOptionEntries .getBuffer (); 241 242// Argument parsing should not have added macros. 243SLANG_ASSERT (sessionDesc .preprocessorMacroCount == 0 ); 244sessionDesc .preprocessorMacroCount = (SlangInt )macros .getCount (); 245sessionDesc .preprocessorMacros = macros .getBuffer (); 246 247ComPtr < slang::ISession > slangSession = nullptr ; 248SLANG_RETURN_ON_FAIL (globalSession -> createSession (sessionDesc ,slangSession .writeRef ())); 249out .m_session = slangSession ; 250out .globalSession = globalSession ; 251 252String source (request .source .dataBegin ,request .source .dataEnd ); 253ComPtr < slang::IBlob > diagnostics ; 254ComPtr < slang::IModule > module (slangSession -> loadModuleFromSourceString ( 255"main" , 256request .source .path , 257source .getBuffer (), 258diagnostics .writeRef ())); 259if (!module ) 260 { 261fprintf ( 262stderr , 263"error: Failed to load module: %s\n" , 264diagnostics ? (char * )diagnostics -> getBufferPointer () :"(no diagnostic output)" ); 265return SLANG_FAIL ; 266 } 267 268// Some tests are verifying that various warnings are printed, so print any diagnostics! 269if (diagnostics && (diagnostics -> getBufferSize ()> 0U )) 270StdWriters ::getError ()."%s" , (char * )diagnostics -> getBufferPointer ()); 271 272ComPtr < slang::IModule > specializedModule ; 273List < ComPtr < slang::IEntryPoint >> specializedEntryPoints ; 274List < slang::IComponentType *> componentsRawPtr ; 275 276ComPtr < ISlangFileSystem > osFileSystem = 277ComPtr < ISlangFileSystem > (Slang ::OSFileSystem ::getExtSingleton ()); 278 279// This list is just kept so that the modules will be freed at scope exit 280List < ComPtr < slang::IModule >> referencedModules ; 281for (auto & path :referencedSlangModulePaths ) 282 { 283auto desc = 284Slang ::ArtifactDescUtil ::getDescFromPath (Slang ::UnownedStringSlice (path .getBuffer ())); 285// If it's a GPU binary, then we'll assume it's a library 286if (ArtifactDescUtil ::isGpuUsable (desc )) 287 { 288desc .kind = ArtifactKind ::Library ; 289 } 290const String name = ArtifactDescUtil ::getBaseNameFromPath (desc ,path .getUnownedSlice ()); 291 292ComPtr < slang::IBlob > codeBlob ; 293SlangResult result = osFileSystem -> loadFile (path .getBuffer (),codeBlob .writeRef ()); 294if (SLANG_FAILED (result )) 295 { 296fprintf (stderr ,"error: Failed to read referenced module file: %s\n" ,path .getBuffer ()); 297return SLANG_FAIL ; 298 } 299 300ComPtr < slang::IModule > module ; 301switch (desc .payload ) 302 { 303case Slang ::ArtifactDesc ::Payload ::Slang : 304 { 305String sourceString ( 306 (const char * )codeBlob -> getBufferPointer (), 307 (const char * )codeBlob -> getBufferPointer ()+ codeBlob -> getBufferSize ()); 308module = ComPtr < slang::IModule > (slangSession -> loadModuleFromSourceString ( 309name .getBuffer (), 310path .getBuffer (), 311sourceString .getBuffer (), 312diagnostics .writeRef ())); 313break ; 314 } 315case Slang ::ArtifactDesc ::Payload ::SlangIR : 316 { 317module = ComPtr < slang::IModule > (slangSession -> loadModuleFromIRBlob ( 318name .getBuffer (), 319path .getBuffer (), 320codeBlob , 321diagnostics .writeRef ())); 322break ; 323 } 324default : 325 { 326SLANG_UNREACHABLE ("Unexpected artifact payload type" ); 327 } 328 } 329 330if (!module ) 331 { 332fprintf ( 333stderr , 334"error: Failed to load referenced module: %s: %s\n" , 335path .getBuffer (), 336diagnostics ? (char * )diagnostics -> getBufferPointer () :"(no diagnostic output)" ); 337return SLANG_FAIL ; 338 } 339referencedModules .add (module ); 340componentsRawPtr .add (module .get ()); 341 } 342 343int globalSpecializationArgCount = int (request .globalSpecializationArgs .getCount ()); 344int moduleSpecializationArgCount = module -> getSpecializationParamCount (); 345if (globalSpecializationArgCount != moduleSpecializationArgCount ) 346 { 347fprintf ( 348stderr , 349"error: The specialization argument count of the request (%d) does not match that of " 350"the module (%d)!\n" , 351globalSpecializationArgCount , 352moduleSpecializationArgCount ); 353return SLANG_FAIL ; 354 } 355List < slang::SpecializationArg > moduleSpecializationArgs ; 356for (int ii = 0 ;ii < globalSpecializationArgCount ;++ ii ) 357 { 358String specializedTypeName = request .globalSpecializationArgs [ii ].getBuffer (); 359 slang::TypeReflection * typeReflection = 360module -> getLayout ()-> findTypeByName (specializedTypeName .getBuffer ()); 361moduleSpecializationArgs .add (slang::SpecializationArg ::fromType (typeReflection )); 362 } 363 364 { 365ComPtr < slang::IBlob > diagnostics ; 366auto res = module -> specialize ( 367moduleSpecializationArgs .getBuffer (), 368moduleSpecializationArgs .getCount (), 369 (slang::IComponentType ** )specializedModule .writeRef (), 370diagnostics .writeRef ()); 371if (SLANG_FAILED (res )) 372 { 373fprintf ( 374stderr , 375"error: Failed to specialize module: %s\n" , 376diagnostics ? (char * )diagnostics -> getBufferPointer () :"(no diagnostic output)" ); 377return res ; 378 } 379 } 380 381Index explicitEntryPointCount = request .entryPoints .getCount (); 382for (Index ee = 0 ;ee < explicitEntryPointCount ;++ ee ) 383 { 384if (options .dontAddDefaultEntryPoints ) 385 { 386// If default entry points are not to be added, then 387// the `request.entryPoints` array should have been 388// left empty. 389// 390SLANG_ASSERT (false); 391 } 392 393auto & entryPointInfo = request .entryPoints [ee ]; 394 395ComPtr < slang::IEntryPoint > entryPoint ; 396ComPtr < slang::IBlob > diagnostics ; 397auto res = module -> findAndCheckEntryPoint ( 398entryPointInfo .name , 399entryPointInfo .slangStage , 400entryPoint .writeRef (), 401diagnostics .writeRef ()); 402if (SLANG_FAILED (res )) 403 { 404fprintf ( 405stderr , 406"error: Failed to find entry point '%s': %s\n" , 407entryPointInfo .name , 408diagnostics ? (char * )diagnostics -> getBufferPointer () :"(no diagnostic output)" ); 409return res ; 410 } 411 412const int entryPointSpecializationArgCount = 413int (request .entryPointSpecializationArgs .getCount ()); 414if (entryPointSpecializationArgCount != entryPoint -> getSpecializationParamCount ()) 415 { 416fprintf ( 417stderr , 418"error: %s\n" , 419"The specialization argument count of the requested entry point does not match " 420"that of the entry point!" ); 421return SLANG_FAIL ; 422 } 423 424List < slang::SpecializationArg > entryPointSpecializationArgs ; 425for (int ii = 0 ;ii < entryPointSpecializationArgCount ;++ ii ) 426 { 427String specializedTypeName = request .entryPointSpecializationArgs [ii ].getBuffer (); 428 slang::TypeReflection * typeReflection = 429module -> getLayout ()-> findTypeByName (specializedTypeName .getBuffer ()); 430entryPointSpecializationArgs .add (slang::SpecializationArg ::fromType (typeReflection )); 431 } 432 433ComPtr < slang::IEntryPoint > specializedEntryPoint ; 434 { 435ComPtr < slang::IBlob > diagnostics ; 436auto res = entryPoint -> specialize ( 437entryPointSpecializationArgs .getBuffer (), 438entryPointSpecializationArgs .getCount (), 439 (slang::IComponentType ** )specializedEntryPoint .writeRef (), 440diagnostics .writeRef ()); 441if (SLANG_FAILED (res )) 442 { 443fprintf ( 444stderr , 445"error: Failed to specialize entry point: %s\n" , 446diagnostics ? (char * )diagnostics -> getBufferPointer () 447 :"(no diagnostic output)" ); 448return res ; 449 } 450 } 451specializedEntryPoints .add (specializedEntryPoint ); 452 } 453 454// If no explicit entry points were provided, check if the module has any 455// defined entry points (e.g., functions marked with [shader(...)] attributes) 456if (explicitEntryPointCount == 0 && !options .dontAddDefaultEntryPoints ) 457 { 458SlangInt32 definedEntryPointCount = module -> getDefinedEntryPointCount (); 459for (SlangInt32 ee = 0 ;ee < definedEntryPointCount ;++ ee ) 460 { 461ComPtr < slang::IEntryPoint > entryPoint ; 462SLANG_RETURN_ON_FAIL (module -> getDefinedEntryPoint (ee ,entryPoint .writeRef ())); 463 464// For now, we'll assume no specialization is needed for discovered entry points 465// If specialization is needed, this would need to be updated 466specializedEntryPoints .add (entryPoint ); 467 } 468 } 469 470if (input .passThrough == SLANG_PASS_THROUGH_NONE ) 471 { 472componentsRawPtr .add (specializedModule ); 473for (auto & specializedEntryPoint :specializedEntryPoints ) 474componentsRawPtr .add (specializedEntryPoint ); 475 } 476 477// This list just makes sure that the components get released 478List < ComPtr < slang::ITypeConformance >> typeConformanceComponents ; 479if (request .typeConformances .getCount ()) 480 { 481auto reflection = module -> getLayout (); 482for (auto & conformance :request .typeConformances ) 483 { 484ComPtr < ISlangBlob > outDiagnostic ; 485auto derivedType = reflection -> findTypeByName (conformance .derivedTypeName .getBuffer ()); 486auto baseType = reflection -> findTypeByName (conformance .baseTypeName .getBuffer ()); 487ComPtr < slang::ITypeConformance > conformanceComponentType ; 488SlangResult res = slangSession -> createTypeConformanceComponentType ( 489derivedType , 490baseType , 491conformanceComponentType .writeRef (), 492conformance .idOverride , 493outDiagnostic .writeRef ()); 494if (SLANG_FAILED (res )) 495 { 496fprintf ( 497stderr , 498"error: Failed to handle type conformances: %s\n" , 499outDiagnostic ? (char * )outDiagnostic -> getBufferPointer () 500 :"(no diagnostic output)" ); 501return res ; 502 } 503typeConformanceComponents .add (conformanceComponentType ); 504componentsRawPtr .add (conformanceComponentType ); 505 } 506 } 507 508ComPtr < slang::IComponentType > linkedSlangProgram ; 509if (componentsRawPtr .getCount ()> 0 ) 510 { 511ComPtr < slang::IComponentType > composite ; 512ComPtr < ISlangBlob > outDiagnostic ; 513SlangResult res = slangSession -> createCompositeComponentType ( 514componentsRawPtr .getBuffer (), 515componentsRawPtr .getCount (), 516composite .writeRef (), 517outDiagnostic .writeRef ()); 518if (SLANG_FAILED (res )) 519 { 520fprintf ( 521stderr , 522"error: Failed to create composite: %s\n" , 523outDiagnostic ? (char * )outDiagnostic -> getBufferPointer () 524 :"(no diagnostic output)" ); 525return res ; 526 } 527res = composite -> link (linkedSlangProgram .writeRef (),outDiagnostic .writeRef ()); 528if (SLANG_FAILED (res )) 529 { 530fprintf ( 531stderr , 532"error: Failed to link program: %s\n" , 533outDiagnostic ? (char * )outDiagnostic -> getBufferPointer () 534 :"(no diagnostic output)" ); 535 } 536 } 537 538out .set (linkedSlangProgram ); 539return SLANG_OK ; 540} 541 542static SlangResult compileProgram ( 543 slang::IGlobalSession * globalSession , 544const Options & options , 545const ShaderCompilerUtil ::Input & input , 546const ShaderCompileRequest & request , 547ShaderCompilerUtil ::Output & out ) 548{ 549if (input .passThrough == SLANG_PASS_THROUGH_NONE ) 550 { 551return _compileProgramImpl (globalSession ,options ,input ,request ,out ); 552 } 553else 554 { 555bool canUseSlangForPrecompile = false; 556switch (input .passThrough ) 557 { 558case SLANG_PASS_THROUGH_DXC : 559case SLANG_PASS_THROUGH_FXC : 560canUseSlangForPrecompile = true; 561break ; 562default : 563break ; 564 } 565// If we are doing a HLSL pass-through compilation, then we can't rely 566// on the downstream compiler for the reflection information that 567// will drive all of our parameter binding. As such, we will first 568// compile with Slang to get reflection information, and then 569// compile in another pass using the desired downstream compiler 570// so that we can get the refleciton information we need. 571// 572ShaderCompilerUtil ::Output slangOutput ; 573if (canUseSlangForPrecompile ) 574 { 575ShaderCompilerUtil ::Input slangInput = input ; 576slangInput .sourceLanguage = SLANG_SOURCE_LANGUAGE_SLANG ; 577slangInput .passThrough = SLANG_PASS_THROUGH_NONE ; 578// TODO: we want to pass along a flag to skip codegen... 579 580 581SLANG_RETURN_ON_FAIL ( 582_compileProgramImpl (globalSession ,options ,slangInput ,request ,slangOutput )); 583 } 584 585// Now we have what we need to be able to do the downstream compile better. 586// 587// TODO: We should be able to use the output from the Slang compilation 588// to fill in the actual entry points to be used for this compilation, 589// so that discovery of entry points via `[shader(...)]` attributes will work. 590// 591SLANG_RETURN_ON_FAIL (_compileProgramImpl (globalSession ,options ,input ,request ,out )); 592 593out .m_session = slangOutput .m_session ; 594// slangOutput.desc.slangGlobalScope and slangOutput.slangProgram are the same object, 595// but the latter is a ComPtr while the former isn't. Therefore we need to detach so 596// that the object doesn't get destroyed. 597SLANG_ASSERT (slangOutput .desc .slangGlobalScope == slangOutput .slangProgram .get ()); 598out .desc .slangGlobalScope = slangOutput .slangProgram .detach (); 599slangOutput .m_session = nullptr ; 600return SLANG_OK ; 601 } 602} 603 604// Helper for compileWithLayout 605/* static */ SlangResult readSource (const String & inSourcePath ,List < char >& outSourceText ) 606{ 607// Read in the source code 608FILE * sourceFile = fopen (inSourcePath .getBuffer (),"rb" ); 609if (!sourceFile ) 610 { 611fprintf (stderr ,"error: failed to open '%s' for reading\n" ,inSourcePath .getBuffer ()); 612return SLANG_FAIL ; 613 } 614fseek (sourceFile ,0 ,SEEK_END ); 615size_t sourceSize = ftell (sourceFile ); 616fseek (sourceFile ,0 ,SEEK_SET ); 617 618outSourceText .setCount (sourceSize + 1 ); 619if (fread (outSourceText .getBuffer (),sourceSize ,1 ,sourceFile )!= 1 ) 620 { 621fprintf (stderr ,"error: failed to read from '%s'\n" ,inSourcePath .getBuffer ()); 622return SLANG_FAIL ; 623 } 624fclose (sourceFile ); 625outSourceText [sourceSize ]= 0 ; 626 627return SLANG_OK ; 628} 629 630/* static */ SlangResult ShaderCompilerUtil ::compileWithLayout ( 631 slang::IGlobalSession * globalSession , 632const Options & options , 633const Input & input , 634ShaderCompilerUtil ::OutputAndLayout & output ) 635{ 636String sourcePath = options .sourcePath ; 637auto shaderType = options .shaderType ; 638 639List < char > sourceText ; 640SLANG_RETURN_ON_FAIL (readSource (sourcePath ,sourceText )); 641 642if (input .sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP || 643input .sourceLanguage == SLANG_SOURCE_LANGUAGE_C ) 644 { 645// Add an include of the prelude 646ComPtr < ISlangBlob > prelude ; 647globalSession -> getLanguagePrelude (input .sourceLanguage ,prelude .writeRef ()); 648 649String preludeString = StringUtil ::getString (prelude ); 650 651// Add the prelude 652StringBuilder builder ; 653builder <<preludeString <<"\n" ; 654builder <<UnownedStringSlice (sourceText .getBuffer (),sourceText .getCount ()); 655 656sourceText .setCount (builder .getLength ()); 657memcpy (sourceText .getBuffer (),builder .getBuffer (),builder .getLength ()); 658 } 659 660output .sourcePath = sourcePath ; 661 662auto & layout = output .layout ; 663 664// Default the amount of renderTargets based on shader type 665switch (shaderType ) 666 { 667default : 668layout .numRenderTargets = 1 ; 669break ; 670 671case Options ::ShaderProgramType ::Compute : 672case Options ::ShaderProgramType ::RayTracing : 673layout .numRenderTargets = 0 ; 674break ; 675 } 676 677// Deterministic random generator 678RefPtr < RandomGenerator > rand = RandomGenerator ::create (0x34234 ); 679 680// Parse the layout 681layout .parse (rand ,sourceText .getBuffer ()); 682 683// Setup SourceInfo 684ShaderCompileRequest ::SourceInfo sourceInfo ; 685sourceInfo .path = sourcePath .getBuffer (); 686sourceInfo .dataBegin = sourceText .getBuffer (); 687// Subtract 1 because it's zero terminated 688sourceInfo .dataEnd = sourceText .getBuffer ()+ sourceText .getCount ()- 1 ; 689 690ShaderCompileRequest compileRequest ; 691 692compileRequest .source = sourceInfo ; 693 694// Now we will add the "default" entry point names/stages that 695// are appropriate to the pipeline type being targetted, *unless* 696// the options specify that we should leave out the default 697// entry points and instead rely on the Slang compiler's built-in 698// mechanisms for discovering entry points (e.g., `[shader(...)]` 699// attributes). 700// 701if (!options .dontAddDefaultEntryPoints ) 702 { 703switch (shaderType ) 704 { 705case Options ::ShaderProgramType ::Graphics : 706case Options ::ShaderProgramType ::GraphicsCompute : 707 { 708ShaderCompileRequest ::EntryPoint vertexEntryPoint ; 709vertexEntryPoint .name = vertexEntryPointName ; 710vertexEntryPoint .slangStage = SLANG_STAGE_VERTEX ; 711compileRequest .entryPoints .add (vertexEntryPoint ); 712 713ShaderCompileRequest ::EntryPoint fragmentEntryPoint ; 714fragmentEntryPoint .name = fragmentEntryPointName ; 715fragmentEntryPoint .slangStage = SLANG_STAGE_FRAGMENT ; 716compileRequest .entryPoints .add (fragmentEntryPoint ); 717 } 718break ; 719case Options ::ShaderProgramType ::GraphicsTaskMeshCompute : 720 { 721ShaderCompileRequest ::EntryPoint taskEntryPoint ; 722taskEntryPoint .name = taskEntryPointName ; 723taskEntryPoint .slangStage = SLANG_STAGE_AMPLIFICATION ; 724compileRequest .entryPoints .add (taskEntryPoint ); 725 } 726 [[fallthrough ]]; 727case Options ::ShaderProgramType ::GraphicsMeshCompute : 728 { 729ShaderCompileRequest ::EntryPoint meshEntryPoint ; 730meshEntryPoint .name = meshEntryPointName ; 731meshEntryPoint .slangStage = SLANG_STAGE_MESH ; 732compileRequest .entryPoints .add (meshEntryPoint ); 733 734ShaderCompileRequest ::EntryPoint fragmentEntryPoint ; 735fragmentEntryPoint .name = fragmentEntryPointName ; 736fragmentEntryPoint .slangStage = SLANG_STAGE_FRAGMENT ; 737compileRequest .entryPoints .add (fragmentEntryPoint ); 738 } 739break ; 740case Options ::ShaderProgramType ::RayTracing : 741 { 742// Note: Current GPU ray tracing pipelines allow for an 743// almost arbitrary mix of entry points for different stages 744// to be used together (e.g., a single "program" might 745// have multiple any-hit shaders, multiple miss shaders, etc.) 746// 747// Rather than try to define a fixed set of entry point 748// names and stages that the testing will support, we will 749// instead rely on `[shader(...)]` annotations to tell us 750// what entry points are present in the input code. 751 } 752break ; 753default : 754 { 755ShaderCompileRequest ::EntryPoint computeEntryPoint ; 756computeEntryPoint .name = computeEntryPointName ; 757computeEntryPoint .slangStage = SLANG_STAGE_COMPUTE ; 758compileRequest .entryPoints .add (computeEntryPoint ); 759 } 760 } 761 } 762compileRequest .globalSpecializationArgs = layout .globalSpecializationArgs ; 763compileRequest .entryPointSpecializationArgs = layout .entryPointSpecializationArgs ; 764for (auto conformance :layout .typeConformances ) 765 { 766ShaderCompileRequest ::TypeConformance c ; 767c .derivedTypeName = conformance .derivedTypeName ; 768c .baseTypeName = conformance .baseTypeName ; 769c .idOverride = conformance .idOverride ; 770compileRequest .typeConformances .add (c ); 771 } 772return compileProgram (globalSession ,options ,input ,compileRequest ,output .output ); 773} 774 775}// namespace renderer_test