yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
134c4c8db
master
1// slang-glslang.cpp 2#include "slang-glslang.h" 3 4#include "SPIRV/GlslangToSpv.h" 5#include "glslang/Public/ShaderLang.h" 6#include "slang.h" 7#include "spirv-tools/libspirv.h" 8#include "spirv-tools/linker.hpp" 9#include "spirv-tools/optimizer.hpp" 10 11#ifdef _WIN32 12#include <windows.h> 13#endif 14 15#include <cassert> 16#include <iostream> 17#include <memory> 18#include <mutex> 19#include <sstream> 20 21// This is a wrapper to allow us to run the `glslang` compiler 22// in a controlled fashion. 23 24#define UNLIMITED 9999 25 26static TBuiltInResource _calcBuiltinResources () 27{ 28// NOTE! This is a bit of a hack - to set all the fields to true/UNLIMITED. 29// Care must be taken if new variables are introduced, the default may not be appropriate. 30 31// We are relying on limits being after the other fields. 32SLANG_COMPILE_TIME_ASSERT (SLANG_OFFSET_OF (TBuiltInResource ,limits )> 0 ); 33// We are relying on maxLights being the first parameter, and all values will have the same type 34SLANG_COMPILE_TIME_ASSERT (SLANG_OFFSET_OF (TBuiltInResource ,maxLights )== 0 ); 35 36TBuiltInResource resource ; 37// Set up all the integer values. 38 { 39 40auto * dst = & resource .maxLights ; 41const size_t count = SLANG_OFFSET_OF (TBuiltInResource ,limits ) /sizeof (* dst ); 42for (size_t i = 0 ;i < count ;++ i ) 43 { 44dst [i ]= UNLIMITED ; 45 } 46 } 47 48// In the sea of variables there is a min value 49resource .minProgramTexelOffset = - UNLIMITED ; 50 51// Set up the bools 52 { 53TLimits * limits = & resource .limits ; 54bool * dst = (bool * )limits ; 55 56const size_t count = sizeof (TLimits ) /sizeof (bool ); 57for (size_t i = 0 ;i < count ;++ i ) 58 { 59dst [i ]= true; 60 } 61 } 62return resource ; 63} 64 65static TBuiltInResource gResources = _calcBuiltinResources (); 66 67static void dump ( 68void const * data , 69size_t size , 70glslang_OutputFunc outputFunc , 71void * outputUserData , 72FILE * fallbackStream ) 73{ 74if (outputFunc ) 75 { 76outputFunc (data ,size ,outputUserData ); 77 } 78else 79 { 80fwrite (data ,1 ,size ,fallbackStream ); 81 82// also output it for debug purposes 83 std::string str ((char const * )data ,size ); 84#ifdef _WIN32 85OutputDebugStringA (str .c_str ()); 86#else 87fprintf (stderr ,"%s\n" ,str .c_str ()); 88 ; 89#endif 90 } 91} 92 93static void dumpDiagnostics (const glslang_CompileRequest_1_2 & request , std::string const & log ) 94{ 95dump (log .c_str (),log .length (),request .diagnosticFunc ,request .diagnosticUserData ,stderr ); 96} 97 98struct SPIRVOptimizationDiagnostic 99{ 100 std::string toString ()const 101 { 102 std::ostringstream out ; 103 104switch (level ) 105 { 106case SPV_MSG_FATAL : 107case SPV_MSG_INTERNAL_ERROR : 108case SPV_MSG_ERROR : 109out <<"error: " ; 110break ; 111case SPV_MSG_WARNING : 112out <<"warning: " ; 113break ; 114case SPV_MSG_INFO : 115case SPV_MSG_DEBUG : 116out <<"info: " ; 117break ; 118default : 119break ; 120 } 121if (source .length ()) 122 { 123out <<source <<":" ; 124 } 125out <<position .line <<":" <<position .column <<":" <<position .index <<":" ; 126if (message .length ()) 127 { 128out <<" " <<message ; 129 } 130 131return out .str (); 132 } 133 134spv_message_level_t level ; 135 std::string source ; 136spv_position_t position ; 137 std::string message ; 138}; 139 140// TODO: the actual printing should happen on the application side. 141static void validationMessageConsumer ( 142spv_message_level_t level , 143const char * , 144const spv_position_t & position , 145const char * message ) 146{ 147switch (level ) 148 { 149case SPV_MSG_FATAL : 150case SPV_MSG_INTERNAL_ERROR : 151case SPV_MSG_ERROR : 152 std::cerr <<"error: line " <<position .index <<": " <<message << std::endl ; 153break ; 154case SPV_MSG_WARNING : 155 std::cout <<"warning: line " <<position .index <<": " <<message << std::endl ; 156break ; 157case SPV_MSG_INFO : 158 std::cout <<"info: line " <<position .index <<": " <<message << std::endl ; 159break ; 160default : 161break ; 162 } 163} 164 165// Validate the given SPIRV-ASM instructions. 166extern "C" 167#ifdef _MSC_VER 168_declspec (dllexport ) 169#else 170 __attribute__((__visibility__ ("default" ))) 171#endif 172bool glslang_validateSPIRV (const uint32_t * contents ,int contentsSize ) 173{ 174spv_target_env target_env = SPV_ENV_UNIVERSAL_1_6 ; 175 176 spvtools::ValidatorOptions options ; 177options .SetScalarBlockLayout (true); 178options .SetFriendlyNames (true); 179 180 spvtools::SpirvTools tools (target_env ); 181tools .SetMessageConsumer (validationMessageConsumer ); 182 183return tools .Validate (contents ,contentsSize ,options ); 184} 185 186// Disassemble the given SPIRV-ASM instructions and return the result as a string. 187extern "C" 188#ifdef _MSC_VER 189_declspec (dllexport ) 190#else 191 __attribute__((__visibility__ ("default" ))) 192#endif 193bool glslang_disassembleSPIRVWithResult ( 194const uint32_t * contents , 195int contentsSize , 196char ** outString ) 197{ 198static const auto kDefaultEnvironment = SPV_ENV_UNIVERSAL_1_5 ; 199spv_text text ; 200 201uint32_t options = SPV_BINARY_TO_TEXT_OPTION_NONE ; 202options |=SPV_BINARY_TO_TEXT_OPTION_COMMENT ; 203options |=SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES ; 204options |=SPV_BINARY_TO_TEXT_OPTION_INDENT ; 205 206spv_diagnostic diagnostic = nullptr ; 207spv_context context = spvContextCreate (kDefaultEnvironment ); 208spv_result_t error = 209spvBinaryToText (context ,contents ,contentsSize ,options ,& text ,& diagnostic ); 210spvContextDestroy (context ); 211if (error ) 212 { 213spvDiagnosticPrint (diagnostic ); 214spvDiagnosticDestroy (diagnostic ); 215return false; 216 } 217else 218 { 219if (outString ) 220 { 221// Allocate memory for the output string and copy the result 222size_t len = text -> length + 1 ;// +1 for null terminator 223* outString = new char [len ]; 224memcpy (* outString ,text -> str ,text -> length ); 225 (* outString )[text -> length ]= '\0' ;// Ensure null termination 226 } 227 228spvTextDestroy (text ); 229return true; 230 } 231} 232 233 234// Disassemble the given SPIRV-ASM instructions. 235extern "C" 236#ifdef _MSC_VER 237_declspec (dllexport ) 238#else 239 __attribute__((__visibility__ ("default" ))) 240#endif 241bool glslang_disassembleSPIRV (const uint32_t * contents ,int contentsSize ) 242{ 243char * result = nullptr ; 244auto succ = glslang_disassembleSPIRVWithResult (contents ,contentsSize ,& result ); 245if (result ) 246fprintf (stdout ,"%s\n" ,result ); 247delete result ; 248return succ ; 249} 250 251// Apply the SPIRV-Tools optimizer to generated SPIR-V based on the desired optimization level 252// TODO: add flag for optimizing SPIR-V size as well 253static void glslang_optimizeSPIRV ( 254spv_target_env targetEnv , 255const glslang_CompileRequest_1_2 & request , 256 std::vector < SPIRVOptimizationDiagnostic >& outDiags , 257 std::vector < unsigned int >& ioSpirv ) 258{ 259const auto optimizationLevel = request .optimizationLevel ; 260 261// If there is no optimization then we are done 262if (optimizationLevel == SLANG_OPTIMIZATION_LEVEL_NONE ) 263 { 264return ; 265 } 266 267const auto debugInfoType = request .debugInfoType ; 268 269 spvtools::Optimizer optimizer (targetEnv ); 270 271optimizer .SetMessageConsumer ( 272 [& ](spv_message_level_t level , 273const char * source , 274const spv_position_t & position , 275const char * message ) 276 { 277SPIRVOptimizationDiagnostic diag ; 278diag .level = level ; 279if (source ) 280 { 281diag .source = source ; 282 } 283diag .position = position ; 284if (message ) 285 { 286diag .message = message ; 287 } 288outDiags .push_back (diag ); 289 }); 290 291// If debug info is being generated, propagate 292// line information into all SPIR-V instructions. This avoids loss of 293// information when instructions are deleted or moved. Later, remove 294// redundant information to minimize final SPRIR-V size. 295if (debugInfoType != SLANG_DEBUG_INFO_LEVEL_NONE ) 296 { 297optimizer .RegisterPass (spvtools::CreatePropagateLineInfoPass ()); 298 } 299 300 spvtools::OptimizerOptions spvOptOptions ; 301 302// To compile some large shaders the default is not enough. 303// That although this limit is exceeded, the final optimized output is typically well 304// within the range. 305// 306// See kDefaultMaxIdBound for description of this limit. 307// 308// If a compilation produces a warning like 309// `0:0: ID overflow. Try running compact-ids.` 310// it might be fixable by raising the multiplier to a larger value. 311spvOptOptions .set_max_id_bound (kDefaultMaxIdBound * 4 ); 312 313// TODO confirm which passes we want to invoke for each level 314switch (optimizationLevel ) 315 { 316default : 317case SLANG_OPTIMIZATION_LEVEL_DEFAULT : 318 { 319// Use a minimal set of performance settings 320// If we run CreateInlineExhaustivePass, We need to run CreateMergeReturnPass first. 321 322#if 0 323// This is the previous 'default optimization' passes setting for glslang 324optimizer .RegisterPass (spvtools::CreateMergeReturnPass ()); 325optimizer .RegisterPass (spvtools::CreateInlineExhaustivePass ()); 326optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 327optimizer .RegisterPass (spvtools::CreatePrivateToLocalPass ()); 328optimizer .RegisterPass (spvtools::CreateScalarReplacementPass (100 )); 329optimizer .RegisterPass (spvtools::CreateLocalAccessChainConvertPass ()); 330optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 331#elif 1 332// 6Mb 27 secs (all passes up to 9) 333// 9Mb 25 secs (all passes up to 7) 334// 8Mb 15 secs (all passes) -(5,6,7) 335// 6Mb 15 secs (all passes) -(6,7) 336 337// This list of passes takes the previous 'default optimization' 338// passes (as listed above) and tries to combine them in order with the 'new' passes 339// below. The issue with the passes below is that although it produces smaller SPIR-V 340// fairly quickly it can cause serious problem on some drivers. 341// 342// Across a wide range of compilations this produced SPIR-V that is less than half size 343// of the previous -O1 passes above. 344 345optimizer .RegisterPass (spvtools::CreateWrapOpKillPass ());// 1 346optimizer .RegisterPass (spvtools::CreateDeadBranchElimPass ());// 2 347 348optimizer .RegisterPass (spvtools::CreateMergeReturnPass ()); 349optimizer .RegisterPass (spvtools::CreateInlineExhaustivePass ()); 350 351optimizer .RegisterPass (spvtools::CreateEliminateDeadFunctionsPass ());// 3 352 353optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 354optimizer .RegisterPass (spvtools::CreatePrivateToLocalPass ()); 355 356optimizer .RegisterPass (spvtools::CreateScalarReplacementPass (100 )); 357 358optimizer .RegisterPass (spvtools::CreateCCPPass ());// 4 * 359optimizer .RegisterPass (spvtools::CreateSimplificationPass ());// 5 360// optimizer.RegisterPass(spvtools::CreateIfConversionPass()); // 6 361// optimizer.RegisterPass(spvtools::CreateBlockMergePass()); // 7 * 362 363optimizer .RegisterPass (spvtools::CreateLocalAccessChainConvertPass ()); 364 365optimizer .RegisterPass (spvtools::CreateLocalSingleBlockLoadStoreElimPass ());// 8 366 367optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 368 369optimizer .RegisterPass (spvtools::CreateVectorDCEPass ());// 9 370 371#else 372// The following selection of passes was created by 373// 1) Taking the list of passes from optimizer.RegisterSizePasses 374// 2) Disable/enable passes to try to produce some reasonable combination of low SPIR-V 375// output size and compilation speed 376// 377// For a particularly difficult glsl shader this produced 1/3 SPIR-V code (against 378// previous -O1), in around 13th the time (against -O3 option) Over a wide range of 379// compiles the SPIR-V is around 6% larger than -O3 380 381// The following comments describe the path to finding this combination. The original 382// compilation produces 18Mb SPIR-V binaries in around 3 1/2 mins. The integer number 383// increases with the ordering of the test. 384// 385// With 5 47s 386// With 6 we have 6Mb, and 38 seconds 387// With 7 we have 6Mb and 26 seconds 388// With 8 we have 6Mb in 18 seconds 389// 9 didn't improve perf or size 390// With 10 we have 6Mb in 16.8 391// With 11 we have 6Mb in 16.1 392// With 12 we have 6Mb in 15.6 393// With 13 didn't improve 394// With 14 slightly larger, slightly smaller, so leave 395// Try 15 - Adding one and removing the other, makes things much worse 396// Without any SSA rewrite we are up to 6Mb. 48 397// 398// So (for test case) approximately 13x compilation speed. 399// Binary twice the size of smallest SPIR-V size and 1/3 the size of the previous -O 400// size 401optimizer .RegisterPass (spvtools::CreateWrapOpKillPass ()); 402optimizer .RegisterPass (spvtools::CreateDeadBranchElimPass ());// 15 403optimizer .RegisterPass (spvtools::CreateMergeReturnPass ()); 404optimizer .RegisterPass (spvtools::CreateInlineExhaustivePass ()); 405optimizer .RegisterPass (spvtools::CreateEliminateDeadFunctionsPass ());// 9 406optimizer .RegisterPass (spvtools::CreatePrivateToLocalPass ()); 407// optimizer.RegisterPass(spvtools::CreateScalarReplacementPass(0)); // 12 408// optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass()); 409optimizer .RegisterPass (spvtools::CreateCCPPass ()); 410// optimizer.RegisterPass(spvtools::CreateLoopUnrollPass(true)); // 1 411// optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); // 4 412// optimizer.RegisterPass(spvtools::CreateSimplificationPass()); // 11 413optimizer .RegisterPass (spvtools::CreateScalarReplacementPass (0 )); 414// optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass()); 415// optimizer.RegisterPass(spvtools::CreateIfConversionPass()); // 7 416optimizer .RegisterPass (spvtools::CreateSimplificationPass ());// 13 417// optimizer.RegisterPass(spvtools::CreateAggressiveDCEPass()); // 10 418// optimizer.RegisterPass(spvtools::CreateDeadBranchElimPass()); // 6 + 15 419// optimizer.RegisterPass(spvtools::CreateBlockMergePass()); // 8 420optimizer .RegisterPass (spvtools::CreateLocalAccessChainConvertPass ()); 421optimizer .RegisterPass (spvtools::CreateLocalSingleBlockLoadStoreElimPass ()); 422optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ());// 5 423// optimizer.RegisterPass(spvtools::CreateCopyPropagateArraysPass()); // 1 424optimizer .RegisterPass (spvtools::CreateVectorDCEPass ()); 425optimizer .RegisterPass (spvtools::CreateDeadInsertElimPass ()); 426optimizer .RegisterPass (spvtools::CreateEliminateDeadMembersPass ()); 427// optimizer.RegisterPass(spvtools::CreateLocalSingleStoreElimPass()); 428// optimizer.RegisterPass(spvtools::CreateBlockMergePass()); // 3 429// optimizer.RegisterPass(spvtools::CreateLocalMultiStoreElimPass()); // 2 430// optimizer.RegisterPass(spvtools::CreateRedundancyEliminationPass()); 431optimizer .RegisterPass (spvtools::CreateSimplificationPass ());// 14 432optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 433optimizer .RegisterPass (spvtools::CreateCFGCleanupPass ()); 434#endif 435 436break ; 437 } 438// TODO(JS): It would be better if we had some distinction here where 'high' meant optimize 439// 'in a reasonable time' for a better optimization, and 'maximal' meant compilation might 440// take a really long time... so only use it if it's really needed. 441// 442// Currently we just have high have the same meaning as 'maximal'. 443case SLANG_OPTIMIZATION_LEVEL_HIGH : 444case SLANG_OPTIMIZATION_LEVEL_MAXIMAL : 445 { 446// Use the same passes when specifying the "-O" flag in spirv-opt 447// Roughly equivalent to `RegisterPerformancePasses` 448 449optimizer .RegisterPass (spvtools::CreateWrapOpKillPass ()); 450optimizer .RegisterPass (spvtools::CreateDeadBranchElimPass ()); 451optimizer .RegisterPass (spvtools::CreateMergeReturnPass ()); 452optimizer .RegisterPass (spvtools::CreateInlineExhaustivePass ()); 453optimizer .RegisterPass (spvtools::CreateEliminateDeadFunctionsPass ()); 454optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 455optimizer .RegisterPass (spvtools::CreatePrivateToLocalPass ()); 456optimizer .RegisterPass (spvtools::CreateLocalSingleBlockLoadStoreElimPass ()); 457optimizer .RegisterPass (spvtools::CreateLocalSingleStoreElimPass ()); 458optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 459optimizer .RegisterPass (spvtools::CreateScalarReplacementPass ()); 460optimizer .RegisterPass (spvtools::CreateLocalAccessChainConvertPass ()); 461optimizer .RegisterPass (spvtools::CreateLocalSingleBlockLoadStoreElimPass ()); 462optimizer .RegisterPass (spvtools::CreateLocalSingleStoreElimPass ()); 463optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 464 465// We run CompactIdsPass here, because CreateLocalMultiStoreElimPass can explode 466// id usage (by a factor of 10), and compacting ids here has been shown to half 467// id usage with a complex shader. 468optimizer .RegisterPass (spvtools::CreateCompactIdsPass ()); 469 470// Note that CreateLocalMultiStoreElimPass really just does a SSARewritePass 471optimizer .RegisterPass (spvtools::CreateLocalMultiStoreElimPass ()); 472 473optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 474optimizer .RegisterPass (spvtools::CreateCCPPass ()); 475optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 476optimizer .RegisterPass (spvtools::CreateLoopUnrollPass (true)); 477optimizer .RegisterPass (spvtools::CreateDeadBranchElimPass ()); 478optimizer .RegisterPass (spvtools::CreateRedundancyEliminationPass ()); 479optimizer .RegisterPass (spvtools::CreateCombineAccessChainsPass ()); 480optimizer .RegisterPass (spvtools::CreateSimplificationPass ()); 481optimizer .RegisterPass (spvtools::CreateScalarReplacementPass ()); 482optimizer .RegisterPass (spvtools::CreateLocalAccessChainConvertPass ()); 483optimizer .RegisterPass (spvtools::CreateLocalSingleBlockLoadStoreElimPass ()); 484optimizer .RegisterPass (spvtools::CreateLocalSingleStoreElimPass ()); 485optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 486optimizer .RegisterPass (spvtools::CreateSSARewritePass ()); 487optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 488optimizer .RegisterPass (spvtools::CreateVectorDCEPass ()); 489optimizer .RegisterPass (spvtools::CreateDeadInsertElimPass ()); 490optimizer .RegisterPass (spvtools::CreateDeadBranchElimPass ()); 491optimizer .RegisterPass (spvtools::CreateSimplificationPass ()); 492optimizer .RegisterPass (spvtools::CreateIfConversionPass ()); 493optimizer .RegisterPass (spvtools::CreateCopyPropagateArraysPass ()); 494optimizer .RegisterPass (spvtools::CreateReduceLoadSizePass ()); 495optimizer .RegisterPass (spvtools::CreateAggressiveDCEPass ()); 496optimizer .RegisterPass (spvtools::CreateBlockMergePass ()); 497optimizer .RegisterPass (spvtools::CreateRedundancyEliminationPass ()); 498optimizer .RegisterPass (spvtools::CreateDeadBranchElimPass ()); 499optimizer .RegisterPass (spvtools::CreateBlockMergePass ()); 500optimizer .RegisterPass (spvtools::CreateSimplificationPass ()); 501 502// We again run compaction to try and ensure the final output uses ids that are in 503// range. On a complex shader, this reduced the amount ids by 5. 504optimizer .RegisterPass (spvtools::CreateCompactIdsPass ()); 505 506break ; 507 } 508 } 509 510if (debugInfoType != SLANG_DEBUG_INFO_LEVEL_NONE ) 511 { 512optimizer .RegisterPass (spvtools::CreateRedundantLineInfoElimPass ()); 513 } 514 515spvOptOptions .set_run_validator (false);// Don't run the validator by default 516 517 { 518// Put the output optimized spirv into optSpirv 519 std::vector < unsigned int > optSpirv ; 520 521// Optimize 522if (optimizer .Run (ioSpirv .data (),ioSpirv .size (),& optSpirv ,spvOptOptions )) 523 { 524assert (optSpirv .size ()> 0 ); 525// Make the ioSpirv the optimized spirv 526ioSpirv .swap (optSpirv ); 527 } 528 } 529} 530 531static int spirv_Optimize_1_2 (const glslang_CompileRequest_1_2 & request ) 532{ 533 std::vector < SPIRVOptimizationDiagnostic > diagnostics ; 534 std::vector < uint32_t > spirvBuffer ; 535size_t inputBlobSize = (char * )request .inputEnd - (char * )request .inputBegin ; 536spirvBuffer .resize (inputBlobSize /sizeof (uint32_t )); 537memcpy (spirvBuffer .data (),request .inputBegin ,inputBlobSize ); 538 539glslang_optimizeSPIRV (SPV_ENV_UNIVERSAL_1_5 ,request ,diagnostics ,spirvBuffer ); 540if (request .outputFunc ) 541 { 542request .outputFunc ( 543spirvBuffer .data (), 544spirvBuffer .size ()* sizeof (uint32_t ), 545request .outputUserData ); 546 } 547if (request .diagnosticFunc ) 548 { 549for (auto & diagnostic :diagnostics ) 550 { 551request .diagnosticFunc ( 552 (void * )diagnostic .message .c_str (), 553diagnostic .message .size ()* sizeof (char ), 554request .diagnosticUserData ); 555 } 556 } 557return SLANG_OK ; 558} 559 560static glslang::EShTargetLanguageVersion _makeTargetLanguageVersion ( 561int majorVersion , 562int minorVersion ) 563{ 564return glslang::EShTargetLanguageVersion ( 565 (uint32_t (majorVersion ) <<16 ) | (uint32_t (minorVersion ) <<8 )); 566} 567 568static glsl_SPIRVVersion _toSPIRVVersion (glslang::EShTargetLanguageVersion version ) 569{ 570glsl_SPIRVVersion ver ; 571ver .patch = 0 ; 572ver .major = uint8_t (uint32_t (version ) >>16 ); 573ver .minor = uint8_t (uint32_t (version ) >>8 ); 574return ver ; 575} 576 577// For working out the targets based on SPIR-V target strings 578 579namespace 580{// anonymous 581 582struct SPRIVTargetInfo 583{ 584const char * name ; 585spv_target_env targetEnv ; 586}; 587 588}// namespace 589 590static const SPRIVTargetInfo kSpirvTargetInfos []= { 591 {"1.0" ,SPV_ENV_UNIVERSAL_1_0 }, 592 {"vk1.0" ,SPV_ENV_VULKAN_1_0 }, 593 {"1.1" ,SPV_ENV_UNIVERSAL_1_1 }, 594 {"cl2.1" ,SPV_ENV_OPENCL_2_1 }, 595 {"cl2.2" ,SPV_ENV_OPENCL_2_2 }, 596 {"gl4.0" ,SPV_ENV_OPENGL_4_0 }, 597 {"gl4.1" ,SPV_ENV_OPENGL_4_1 }, 598 {"gl4.2" ,SPV_ENV_OPENGL_4_2 }, 599 {"gl4.3" ,SPV_ENV_OPENGL_4_3 }, 600 {"gl4.5" ,SPV_ENV_OPENGL_4_5 }, 601 {"1.2" ,SPV_ENV_UNIVERSAL_1_2 }, 602 {"cl1.2" ,SPV_ENV_OPENCL_1_2 }, 603 {"cl_emb1.2" ,SPV_ENV_OPENCL_EMBEDDED_1_2 }, 604 {"cl2.0" ,SPV_ENV_OPENCL_2_0 }, 605 {"cl_emb2.0" ,SPV_ENV_OPENCL_EMBEDDED_2_0 }, 606 {"cl_emb2.1" ,SPV_ENV_OPENCL_EMBEDDED_2_1 }, 607 {"cl_emb2.2" ,SPV_ENV_OPENCL_EMBEDDED_2_2 }, 608 {"1.3" ,SPV_ENV_UNIVERSAL_1_3 }, 609 {"vk1.1" ,SPV_ENV_VULKAN_1_1 }, 610 {"web_gpu1.0" ,SPV_ENV_WEBGPU_0 }, 611 {"1.4" ,SPV_ENV_UNIVERSAL_1_4 }, 612 {"vk1.1_spirv1.4" ,SPV_ENV_VULKAN_1_1_SPIRV_1_4 }, 613 {"1.5" ,SPV_ENV_UNIVERSAL_1_5 }, 614}; 615 616static int _findTargetIndex (const char * name ) 617{ 618const int count = int (sizeof (kSpirvTargetInfos ) /sizeof (kSpirvTargetInfos [0 ])); 619for (int i = 0 ;i < count ;++ i ) 620 { 621const SPRIVTargetInfo & info = kSpirvTargetInfos [i ]; 622 623if (::strcmp (info .name ,name )== 0 ) 624 { 625return i ; 626 } 627 } 628return -1 ; 629} 630 631static spv_target_env _getUniversalTargetEnv (glslang::EShTargetLanguageVersion inVersion ) 632{ 633glsl_SPIRVVersion spirvVersion = _toSPIRVVersion (inVersion ); 634uint32_t ver = (uint32_t (spirvVersion .major ) <<8 ) |spirvVersion .minor ; 635 636switch (ver ) 637 { 638case 0x100 : 639return SPV_ENV_UNIVERSAL_1_0 ; 640case 0x101 : 641return SPV_ENV_UNIVERSAL_1_1 ; 642case 0x102 : 643return SPV_ENV_UNIVERSAL_1_2 ; 644case 0x103 : 645return SPV_ENV_UNIVERSAL_1_3 ; 646case 0x104 : 647return SPV_ENV_UNIVERSAL_1_4 ; 648case 0x105 : 649return SPV_ENV_UNIVERSAL_1_5 ; 650case 0x106 : 651return SPV_ENV_UNIVERSAL_1_6 ; 652default : 653 { 654if (ver > 0x106 ) 655 { 656// This is the highest we known for now..., so try that 657return SPV_ENV_UNIVERSAL_1_6 ; 658 } 659break ; 660 } 661 } 662// Just use the default... 663return SPV_ENV_UNIVERSAL_1_2 ; 664} 665 666static int glslang_compileGLSLToSPIRV (glslang_CompileRequest_1_2 request ) 667{ 668// Check that the encoding matches 669assert (glslang::EShTargetSpv_1_4 == _makeTargetLanguageVersion (1 ,4 )); 670 671EShLanguage glslangStage ; 672switch (request .slangStage ) 673 { 674#define CASE (SP ,GL ) \ 675 case SLANG_STAGE_##SP: \ 676 glslangStage = EShLang##GL; \ 677 break 678CASE (VERTEX ,Vertex ); 679CASE (FRAGMENT ,Fragment ); 680CASE (GEOMETRY ,Geometry ); 681CASE (HULL ,TessControl ); 682CASE (DOMAIN ,TessEvaluation ); 683CASE (COMPUTE ,Compute ); 684 685CASE (RAY_GENERATION ,RayGenNV ); 686CASE (INTERSECTION ,IntersectNV ); 687CASE (ANY_HIT ,AnyHitNV ); 688CASE (CLOSEST_HIT ,ClosestHitNV ); 689CASE (MISS ,MissNV ); 690CASE (CALLABLE ,CallableNV ); 691 692CASE (MESH ,Mesh ); 693CASE (AMPLIFICATION ,Task ); 694#undef CASE 695 696default : 697dumpDiagnostics (request ,"internal error: stage unsupported by glslang\n" ); 698return 1 ; 699 } 700 701spv_target_env targetEnv = SPV_ENV_UNIVERSAL_1_2 ; 702 glslang::EShTargetLanguageVersion targetLanguage = glslang::EShTargetLanguageVersion (0 ); 703 704int spirvTargetIndex = -1 ; 705if (request .spirvTargetName ) 706 { 707spirvTargetIndex = _findTargetIndex (request .spirvTargetName ); 708if (spirvTargetIndex < 0 ) 709 { 710dumpDiagnostics (request ,"warning: unknown SPIR-V version\n" ); 711 } 712else 713 { 714targetEnv = kSpirvTargetInfos [spirvTargetIndex ].targetEnv ; 715 } 716 } 717 718// If a version is specified, and no target language is specified, set to universal version of 719// that SPIR-V version 720if (request .spirvVersion .major != 0 && targetLanguage == glslang::EShTargetLanguageVersion (0 )) 721 { 722targetLanguage = 723_makeTargetLanguageVersion (request .spirvVersion .major ,request .spirvVersion .minor ); 724 } 725 726// If we don't have a target, but do have a language, use that to determine a universal target 727if (spirvTargetIndex < 0 && targetLanguage != glslang::EShTargetLanguageVersion (0 )) 728 { 729// We can just use the appropriate universal based on the target language 730targetEnv = _getUniversalTargetEnv (targetLanguage ); 731 } 732 733// TODO: compute glslang stage to use 734 735 glslang::TShader * shader = new glslang::TShader (glslangStage ); 736auto shaderPtr = std::unique_ptr < glslang::TShader > (shader ); 737 738// Only set the target language if one is determined 739if (targetLanguage != glslang::EShTargetLanguageVersion (0 )) 740 { 741shader -> setEnvTarget (glslang::EShTargetSpv ,targetLanguage ); 742 } 743 744 glslang::TProgram * program = new glslang::TProgram (); 745auto programPtr = std::unique_ptr < glslang::TProgram > (program ); 746 747char const * sourceText = (char const * )request .inputBegin ; 748char const * sourceTextEnd = (char const * )request .inputEnd ; 749 750int sourceTextLength = (int )(sourceTextEnd - sourceText ); 751 752shader -> setPreamble ("#extension GL_GOOGLE_cpp_style_line_directive : require\n" ); 753shader -> setStringsWithLengthsAndNames (& sourceText ,& sourceTextLength ,& request .sourcePath ,1 ); 754 755// Options for compilation of glsl to Spv 756 757// spvOptions ctors with default options (this it the same as passing nullptr to GlslangToSpv) 758 glslang::SpvOptions spvOptions ; 759 760const SlangDebugInfoLevel debugLevel = (SlangDebugInfoLevel )request .debugInfoType ; 761 762// Enable generation of debug info, if any debug level other than none is requested 763if (debugLevel != SLANG_DEBUG_INFO_LEVEL_NONE ) 764 { 765spvOptions .generateDebugInfo = true; 766spvOptions .emitNonSemanticShaderDebugInfo = true; 767shader -> setDebugInfo (true); 768 } 769 770if (debugLevel == SLANG_DEBUG_INFO_LEVEL_MAXIMAL ) 771 { 772spvOptions .emitNonSemanticShaderDebugSource = true; 773spvOptions .disableOptimizer = true; 774request .optimizationLevel = SLANG_OPTIMIZATION_LEVEL_NONE ; 775 } 776 777// Link program 778 { 779const EShMessages messages = EShMessages (EShMsgSpvRules |EShMsgVulkanRules ); 780 781if (!shader -> parse (& gResources ,110 , false,messages )) 782 { 783dumpDiagnostics (request ,shader -> getInfoLog ()); 784return 1 ; 785 } 786 787if (request .entryPointName && strlen (request .entryPointName )) 788shader -> setEntryPoint (request .entryPointName ); 789 790program -> addShader (shader ); 791 792if (!program -> link (messages )) 793 { 794dumpDiagnostics (request ,program -> getInfoLog ()); 795return 1 ; 796 } 797 798if (!program -> mapIO ()) 799 { 800dumpDiagnostics (request ,program -> getInfoLog ()); 801return 1 ; 802 } 803 } 804 805for (int stage = 0 ;stage < EShLangCount ;++ stage ) 806 { 807auto stageIntermediate = program -> getIntermediate ((EShLanguage )stage ); 808if (!stageIntermediate ) 809continue ; 810if (debugLevel == SLANG_DEBUG_INFO_LEVEL_MAXIMAL ) 811 { 812shader -> addSourceText (sourceText ,sourceTextLength ); 813 } 814 815 std::vector < unsigned int > spirv ; 816 spv::SpvBuildLogger logger ; 817 818// Copy options to make sure spvOptions not altered 819 glslang::SpvOptions copySpvOptions (spvOptions ); 820 821 glslang::GlslangToSpv (* stageIntermediate ,spirv ,& logger ,& copySpvOptions ); 822 823int optErrorCount = 0 ; 824 825if (request .optimizationLevel != SLANG_OPTIMIZATION_LEVEL_NONE ) 826 { 827 std::vector < SPIRVOptimizationDiagnostic > optDiags ; 828glslang_optimizeSPIRV (targetEnv ,request ,optDiags ,spirv ); 829 830 { 831for (const auto & diag :optDiags ) 832 { 833// Count the number of errors 834optErrorCount += int (diag .level <=SPV_MSG_ERROR ); 835 836// Note this string does not have \n. 837 std::string diagString = diag .toString (); 838 839// Dump 840dump ( 841diagString .c_str (), 842diagString .length (), 843request .diagnosticFunc , 844request .diagnosticUserData , 845stderr ); 846 } 847 } 848 } 849 850dumpDiagnostics (request ,logger .getAllMessages ()); 851 852dump ( 853spirv .data (), 854spirv .size ()* sizeof (unsigned int ), 855request .outputFunc , 856request .outputUserData , 857stdout ); 858 859if (optErrorCount > 0 ) 860 { 861// It's an error... 862return 1 ; 863 } 864 } 865 866return 0 ; 867} 868 869static int glslang_dissassembleSPIRV (const glslang_CompileRequest_1_2 & request ) 870{ 871typedef unsigned int SPIRVWord ; 872 873SPIRVWord const * spirvBegin = (SPIRVWord const * )request .inputBegin ; 874SPIRVWord const * spirvEnd = (SPIRVWord const * )request .inputEnd ; 875 876 std::vector < SPIRVWord > spirv (spirvBegin ,spirvEnd ); 877 878 std::string result ; 879 spvtools::SpirvTools spirvTools (SPV_ENV_UNIVERSAL_1_5 ); 880spirvTools .Disassemble ( 881spirv , 882& result , 883SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |SPV_BINARY_TO_TEXT_OPTION_COMMENT ); 884 885dump (result .c_str (),result .length (),request .outputFunc ,request .outputUserData ,stdout ); 886return 0 ; 887} 888 889// We need a per process initialization 890class ProcessInitializer 891{ 892public : 893ProcessInitializer () {m_isInitialized = false; } 894 895bool init () 896 { 897 std::lock_guard < std::mutex > guard (m_mutex ); 898if (!m_isInitialized ) 899 { 900if (!glslang::InitializeProcess ()) 901 { 902return false; 903 } 904m_isInitialized = true; 905 } 906return true; 907 } 908 909 ~ProcessInitializer () 910 { 911// We *assume* will only be called once dll is detatched and that will be on a single thread 912if (m_isInitialized ) 913 { 914 glslang::FinalizeProcess (); 915 } 916 } 917 918 std::mutex m_mutex ; 919bool m_isInitialized = false; 920}; 921 922static int _compile (const glslang_CompileRequest_1_2 & request ) 923{ 924int result = 0 ; 925switch (request .action ) 926 { 927default : 928result = 1 ; 929break ; 930 931case GLSLANG_ACTION_COMPILE_GLSL_TO_SPIRV : 932result = glslang_compileGLSLToSPIRV (request ); 933break ; 934 935case GLSLANG_ACTION_DISSASSEMBLE_SPIRV : 936result = glslang_dissassembleSPIRV (request ); 937break ; 938 939case GLSLANG_ACTION_OPTIMIZE_SPIRV : 940result = spirv_Optimize_1_2 (request ); 941break ; 942 } 943 944return result ; 945} 946 947extern "C" 948#ifdef _MSC_VER 949_declspec (dllexport ) 950#else 951 __attribute__((__visibility__ ("default" ))) 952#endif 953int glslang_compile_1_2 (glslang_CompileRequest_1_2 * inRequest ) 954{ 955static ProcessInitializer g_processInitializer ; 956if (!g_processInitializer .init ()) 957 { 958// Failed 959return 1 ; 960 } 961 962// If it's the right size just use it 963if (inRequest -> sizeInBytes == sizeof (glslang_CompileRequest_1_2 )) 964 { 965return _compile (* inRequest ); 966 } 967else 968 { 969// NOTE! It could be larger, but here we'll assume thats ok, and copy and use. 970 971// Try to ensure some binary compatibility, by using sizeInBytes member, and copying 972 973glslang_CompileRequest_1_2 request ; 974 975// Copy into request 976const size_t copySize = 977 (inRequest -> sizeInBytes > sizeof (request )) ?sizeof (request ) :inRequest -> sizeInBytes ; 978 ::memcpy (& request ,inRequest ,copySize ); 979// Zero any remaining members 980memset (((uint8_t * )& request )+ copySize ,0 ,sizeof (request )- copySize ); 981 982return _compile (request ); 983 } 984} 985 986extern "C" 987#ifdef _MSC_VER 988_declspec (dllexport ) 989#else 990 __attribute__((__visibility__ ("default" ))) 991#endif 992int glslang_compile_1_1 (glslang_CompileRequest_1_1 * inRequest ) 993{ 994glslang_CompileRequest_1_2 request ; 995memset (& request ,0 ,sizeof (request )); 996request .sizeInBytes = sizeof (request ); 997request .set (* inRequest ); 998return glslang_compile_1_2 (& request ); 999} 1000 1001extern "C" 1002#ifdef _MSC_VER 1003_declspec (dllexport ) 1004#else 1005 __attribute__((__visibility__ ("default" ))) 1006#endif 1007int glslang_compile (glslang_CompileRequest_1_0 * inRequest ) 1008{ 1009glslang_CompileRequest_1_1 request ; 1010memset (& request ,0 ,sizeof (request )); 1011request .sizeInBytes = sizeof (request ); 1012request .set (* inRequest ); 1013return glslang_compile_1_1 (& request ); 1014} 1015 1016extern "C" 1017#ifdef _MSC_VER 1018_declspec (dllexport ) 1019#else 1020 __attribute__((__visibility__ ("default" ))) 1021#endif 1022int glslang_linkSPIRV (glslang_LinkRequest * request ) 1023{ 1024if (!request || !request -> modules || request -> linkResult ) 1025return false; 1026 1027try 1028 { 1029 spvtools::Context context (SPV_ENV_UNIVERSAL_1_5 ); 1030 spvtools::LinkerOptions options = {}; 1031 1032options .SetUseHighestVersion (true); 1033 1034 spvtools::MessageConsumer consumer = [](spv_message_level_t level , 1035const char * source , 1036const spv_position_t & position , 1037const char * message ) 1038 { 1039printf ("SPIRV-TOOLS: %s\n" ,message ); 1040printf ("SPIRV-TOOLS: %s\n" ,source ); 1041printf ("SPIRV-TOOLS: %zu:%zu\n" ,position .index ,position .column ); 1042 }; 1043context .SetMessageConsumer (consumer ); 1044 1045 std::vector < std::vector < uint32_t >> moduleVecs (request -> moduleCount ); 1046 std::vector < const uint32_t *> moduleData (request -> moduleCount ); 1047 std::vector < size_t > moduleSizes (request -> moduleCount ); 1048 1049for (size_t i = 0 ;i < request -> moduleCount ;++ i ) 1050 { 1051moduleData [i ]= request -> modules [i ]; 1052moduleSizes [i ]= request -> moduleSizes [i ]; 1053 } 1054 1055 std::vector < uint32_t > linkedBinary ; 1056spv_result_t success = spvtools::Link ( 1057context , 1058moduleData .data (), 1059moduleSizes .data (), 1060request -> moduleCount , 1061& linkedBinary , 1062options ); 1063 1064if (success == SPV_SUCCESS ) 1065 { 1066request -> linkResult = new uint32_t [linkedBinary .size ()]; 1067memcpy ( 1068 (void * )request -> linkResult , 1069linkedBinary .data (), 1070linkedBinary .size ()* sizeof (uint32_t )); 1071request -> linkResultSize = linkedBinary .size (); 1072 } 1073 1074return success == SPV_SUCCESS ; 1075 } 1076catch (...) 1077 { 1078return false; 1079 } 1080}