1 // 2 // Copyright (C) 2002-2005 3Dlabs Inc. Ltd. 3 // Copyright (C) 2013-2016 LunarG, Inc. 4 // 5 // All rights reserved. 6 // 7 // Redistribution and use in source and binary forms, with or without 8 // modification, are permitted provided that the following conditions 9 // are met: 10 // 11 // Redistributions of source code must retain the above copyright 12 // notice, this list of conditions and the following disclaimer. 13 // 14 // Redistributions in binary form must reproduce the above 15 // copyright notice, this list of conditions and the following 16 // disclaimer in the documentation and/or other materials provided 17 // with the distribution. 18 // 19 // Neither the name of 3Dlabs Inc. Ltd. nor the names of its 20 // contributors may be used to endorse or promote products derived 21 // from this software without specific prior written permission. 22 // 23 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 28 // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 29 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 30 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 33 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 // POSSIBILITY OF SUCH DAMAGE. 35 // 36 37 // this only applies to the standalone wrapper, not the front end in general 38 #ifndef _CRT_SECURE_NO_WARNINGS 39 #define _CRT_SECURE_NO_WARNINGS 40 #endif 41 42 #include "ResourceLimits.h" 43 #include "Worklist.h" 44 #include "DirStackFileIncluder.h" 45 #include "./../glslang/Include/ShHandle.h" 46 #include "./../glslang/Include/revision.h" 47 #include "./../glslang/Public/ShaderLang.h" 48 #include "../SPIRV/GlslangToSpv.h" 49 #include "../SPIRV/GLSL.std.450.h" 50 #include "../SPIRV/doc.h" 51 #include "../SPIRV/disassemble.h" 52 53 #include <cstring> 54 #include <cstdlib> 55 #include <cctype> 56 #include <cmath> 57 #include <array> 58 #include <map> 59 #include <memory> 60 #include <thread> 61 62 #include "../glslang/OSDependent/osinclude.h" 63 64 extern "C" { 65 SH_IMPORT_EXPORT void ShOutputHtml(); 66 } 67 68 // Command-line options 69 enum TOptions { 70 EOptionNone = 0, 71 EOptionIntermediate = (1 << 0), 72 EOptionSuppressInfolog = (1 << 1), 73 EOptionMemoryLeakMode = (1 << 2), 74 EOptionRelaxedErrors = (1 << 3), 75 EOptionGiveWarnings = (1 << 4), 76 EOptionLinkProgram = (1 << 5), 77 EOptionMultiThreaded = (1 << 6), 78 EOptionDumpConfig = (1 << 7), 79 EOptionDumpReflection = (1 << 8), 80 EOptionSuppressWarnings = (1 << 9), 81 EOptionDumpVersions = (1 << 10), 82 EOptionSpv = (1 << 11), 83 EOptionHumanReadableSpv = (1 << 12), 84 EOptionVulkanRules = (1 << 13), 85 EOptionDefaultDesktop = (1 << 14), 86 EOptionOutputPreprocessed = (1 << 15), 87 EOptionOutputHexadecimal = (1 << 16), 88 EOptionReadHlsl = (1 << 17), 89 EOptionCascadingErrors = (1 << 18), 90 EOptionAutoMapBindings = (1 << 19), 91 EOptionFlattenUniformArrays = (1 << 20), 92 EOptionNoStorageFormat = (1 << 21), 93 EOptionKeepUncalled = (1 << 22), 94 EOptionHlslOffsets = (1 << 23), 95 EOptionHlslIoMapping = (1 << 24), 96 EOptionAutoMapLocations = (1 << 25), 97 EOptionDebug = (1 << 26), 98 EOptionStdin = (1 << 27), 99 EOptionOptimizeDisable = (1 << 28), 100 EOptionOptimizeSize = (1 << 29), 101 EOptionInvertY = (1 << 30), 102 EOptionDumpBareVersion = (1 << 31), 103 }; 104 bool targetHlslFunctionality1 = false; 105 bool SpvToolsDisassembler = false; 106 bool SpvToolsValidate = false; 107 108 // 109 // Return codes from main/exit(). 110 // 111 enum TFailCode { 112 ESuccess = 0, 113 EFailUsage, 114 EFailCompile, 115 EFailLink, 116 EFailCompilerCreate, 117 EFailThreadCreate, 118 EFailLinkerCreate 119 }; 120 121 // 122 // Forward declarations. 123 // 124 EShLanguage FindLanguage(const std::string& name, bool parseSuffix=true); 125 void CompileFile(const char* fileName, ShHandle); 126 void usage(); 127 char* ReadFileData(const char* fileName); 128 void FreeFileData(char* data); 129 void InfoLogMsg(const char* msg, const char* name, const int num); 130 131 // Globally track if any compile or link failure. 132 bool CompileFailed = false; 133 bool LinkFailed = false; 134 135 // array of unique places to leave the shader names and infologs for the asynchronous compiles 136 std::vector<std::unique_ptr<glslang::TWorkItem>> WorkItems; 137 138 TBuiltInResource Resources; 139 std::string ConfigFile; 140 141 // 142 // Parse either a .conf file provided by the user or the default from glslang::DefaultTBuiltInResource 143 // 144 void ProcessConfigFile() 145 { 146 if (ConfigFile.size() == 0) 147 Resources = glslang::DefaultTBuiltInResource; 148 else { 149 char* configString = ReadFileData(ConfigFile.c_str()); 150 glslang::DecodeResourceLimits(&Resources, configString); 151 FreeFileData(configString); 152 } 153 } 154 155 int Options = 0; 156 const char* ExecutableName = nullptr; 157 const char* binaryFileName = nullptr; 158 const char* entryPointName = nullptr; 159 const char* sourceEntryPointName = nullptr; 160 const char* shaderStageName = nullptr; 161 const char* variableName = nullptr; 162 bool HlslEnable16BitTypes = false; 163 bool HlslDX9compatible = false; 164 std::vector<std::string> IncludeDirectoryList; 165 166 // Source environment 167 // (source 'Client' is currently the same as target 'Client') 168 int ClientInputSemanticsVersion = 100; 169 170 // Target environment 171 glslang::EShClient Client = glslang::EShClientNone; // will stay EShClientNone if only validating 172 glslang::EShTargetClientVersion ClientVersion; // not valid until Client is set 173 glslang::EShTargetLanguage TargetLanguage = glslang::EShTargetNone; 174 glslang::EShTargetLanguageVersion TargetVersion; // not valid until TargetLanguage is set 175 176 std::vector<std::string> Processes; // what should be recorded by OpModuleProcessed, or equivalent 177 178 // Per descriptor-set binding base data 179 typedef std::map<unsigned int, unsigned int> TPerSetBaseBinding; 180 181 std::vector<std::pair<std::string, int>> uniformLocationOverrides; 182 int uniformBase = 0; 183 184 std::array<std::array<unsigned int, EShLangCount>, glslang::EResCount> baseBinding; 185 std::array<std::array<TPerSetBaseBinding, EShLangCount>, glslang::EResCount> baseBindingForSet; 186 std::array<std::vector<std::string>, EShLangCount> baseResourceSetBinding; 187 188 // Add things like "#define ..." to a preamble to use in the beginning of the shader. 189 class TPreamble { 190 public: 191 TPreamble() { } 192 193 bool isSet() const { return text.size() > 0; } 194 const char* get() const { return text.c_str(); } 195 196 // #define... 197 void addDef(std::string def) 198 { 199 text.append("#define "); 200 fixLine(def); 201 202 Processes.push_back("D"); 203 Processes.back().append(def); 204 205 // The first "=" needs to turn into a space 206 const size_t equal = def.find_first_of("="); 207 if (equal != def.npos) 208 def[equal] = ' '; 209 210 text.append(def); 211 text.append("\n"); 212 } 213 214 // #undef... 215 void addUndef(std::string undef) 216 { 217 text.append("#undef "); 218 fixLine(undef); 219 220 Processes.push_back("U"); 221 Processes.back().append(undef); 222 223 text.append(undef); 224 text.append("\n"); 225 } 226 227 protected: 228 void fixLine(std::string& line) 229 { 230 // Can't go past a newline in the line 231 const size_t end = line.find_first_of("\n"); 232 if (end != line.npos) 233 line = line.substr(0, end); 234 } 235 236 std::string text; // contents of preamble 237 }; 238 239 TPreamble UserPreamble; 240 241 // 242 // Create the default name for saving a binary if -o is not provided. 243 // 244 const char* GetBinaryName(EShLanguage stage) 245 { 246 const char* name; 247 if (binaryFileName == nullptr) { 248 switch (stage) { 249 case EShLangVertex: name = "vert.spv"; break; 250 case EShLangTessControl: name = "tesc.spv"; break; 251 case EShLangTessEvaluation: name = "tese.spv"; break; 252 case EShLangGeometry: name = "geom.spv"; break; 253 case EShLangFragment: name = "frag.spv"; break; 254 case EShLangCompute: name = "comp.spv"; break; 255 #ifdef NV_EXTENSIONS 256 case EShLangRayGenNV: name = "rgen.spv"; break; 257 case EShLangIntersectNV: name = "rint.spv"; break; 258 case EShLangAnyHitNV: name = "rahit.spv"; break; 259 case EShLangClosestHitNV: name = "rchit.spv"; break; 260 case EShLangMissNV: name = "rmiss.spv"; break; 261 case EShLangCallableNV: name = "rcall.spv"; break; 262 case EShLangMeshNV: name = "mesh.spv"; break; 263 case EShLangTaskNV: name = "task.spv"; break; 264 #endif 265 default: name = "unknown"; break; 266 } 267 } else 268 name = binaryFileName; 269 270 return name; 271 } 272 273 // 274 // *.conf => this is a config file that can set limits/resources 275 // 276 bool SetConfigFile(const std::string& name) 277 { 278 if (name.size() < 5) 279 return false; 280 281 if (name.compare(name.size() - 5, 5, ".conf") == 0) { 282 ConfigFile = name; 283 return true; 284 } 285 286 return false; 287 } 288 289 // 290 // Give error and exit with failure code. 291 // 292 void Error(const char* message) 293 { 294 fprintf(stderr, "%s: Error %s (use -h for usage)\n", ExecutableName, message); 295 exit(EFailUsage); 296 } 297 298 // 299 // Process an optional binding base of one the forms: 300 // --argname [stage] base // base for stage (if given) or all stages (if not) 301 // --argname [stage] [base set]... // set/base pairs: set the base for given binding set. 302 303 // Where stage is one of the forms accepted by FindLanguage, and base is an integer 304 // 305 void ProcessBindingBase(int& argc, char**& argv, glslang::TResourceType res) 306 { 307 if (argc < 2) 308 usage(); 309 310 EShLanguage lang = EShLangCount; 311 int singleBase = 0; 312 TPerSetBaseBinding perSetBase; 313 int arg = 1; 314 315 // Parse stage, if given 316 if (!isdigit(argv[arg][0])) { 317 if (argc < 3) // this form needs one more argument 318 usage(); 319 320 lang = FindLanguage(argv[arg++], false); 321 } 322 323 if ((argc - arg) > 2 && isdigit(argv[arg+0][0]) && isdigit(argv[arg+1][0])) { 324 // Parse a per-set binding base 325 while ((argc - arg) > 2 && isdigit(argv[arg+0][0]) && isdigit(argv[arg+1][0])) { 326 const int baseNum = atoi(argv[arg++]); 327 const int setNum = atoi(argv[arg++]); 328 perSetBase[setNum] = baseNum; 329 } 330 } else { 331 // Parse single binding base 332 singleBase = atoi(argv[arg++]); 333 } 334 335 argc -= (arg-1); 336 argv += (arg-1); 337 338 // Set one or all languages 339 const int langMin = (lang < EShLangCount) ? lang+0 : 0; 340 const int langMax = (lang < EShLangCount) ? lang+1 : EShLangCount; 341 342 for (int lang = langMin; lang < langMax; ++lang) { 343 if (!perSetBase.empty()) 344 baseBindingForSet[res][lang].insert(perSetBase.begin(), perSetBase.end()); 345 else 346 baseBinding[res][lang] = singleBase; 347 } 348 } 349 350 void ProcessResourceSetBindingBase(int& argc, char**& argv, std::array<std::vector<std::string>, EShLangCount>& base) 351 { 352 if (argc < 2) 353 usage(); 354 355 if (!isdigit(argv[1][0])) { 356 if (argc < 3) // this form needs one more argument 357 usage(); 358 359 // Parse form: --argname stage [regname set base...], or: 360 // --argname stage set 361 const EShLanguage lang = FindLanguage(argv[1], false); 362 363 argc--; 364 argv++; 365 366 while (argc > 1 && argv[1] != nullptr && argv[1][0] != '-') { 367 base[lang].push_back(argv[1]); 368 369 argc--; 370 argv++; 371 } 372 373 // Must have one arg, or a multiple of three (for [regname set binding] triples) 374 if (base[lang].size() != 1 && (base[lang].size() % 3) != 0) 375 usage(); 376 377 } else { 378 // Parse form: --argname set 379 for (int lang=0; lang<EShLangCount; ++lang) 380 base[lang].push_back(argv[1]); 381 382 argc--; 383 argv++; 384 } 385 } 386 387 // 388 // Do all command-line argument parsing. This includes building up the work-items 389 // to be processed later, and saving all the command-line options. 390 // 391 // Does not return (it exits) if command-line is fatally flawed. 392 // 393 void ProcessArguments(std::vector<std::unique_ptr<glslang::TWorkItem>>& workItems, int argc, char* argv[]) 394 { 395 for (int res = 0; res < glslang::EResCount; ++res) 396 baseBinding[res].fill(0); 397 398 ExecutableName = argv[0]; 399 workItems.reserve(argc); 400 401 const auto bumpArg = [&]() { 402 if (argc > 0) { 403 argc--; 404 argv++; 405 } 406 }; 407 408 // read a string directly attached to a single-letter option 409 const auto getStringOperand = [&](const char* desc) { 410 if (argv[0][2] == 0) { 411 printf("%s must immediately follow option (no spaces)\n", desc); 412 exit(EFailUsage); 413 } 414 return argv[0] + 2; 415 }; 416 417 // read a number attached to a single-letter option 418 const auto getAttachedNumber = [&](const char* desc) { 419 int num = atoi(argv[0] + 2); 420 if (num == 0) { 421 printf("%s: expected attached non-0 number\n", desc); 422 exit(EFailUsage); 423 } 424 return num; 425 }; 426 427 // minimum needed (without overriding something else) to target Vulkan SPIR-V 428 const auto setVulkanSpv = []() { 429 if (Client == glslang::EShClientNone) 430 ClientVersion = glslang::EShTargetVulkan_1_0; 431 Client = glslang::EShClientVulkan; 432 Options |= EOptionSpv; 433 Options |= EOptionVulkanRules; 434 Options |= EOptionLinkProgram; 435 }; 436 437 // minimum needed (without overriding something else) to target OpenGL SPIR-V 438 const auto setOpenGlSpv = []() { 439 if (Client == glslang::EShClientNone) 440 ClientVersion = glslang::EShTargetOpenGL_450; 441 Client = glslang::EShClientOpenGL; 442 Options |= EOptionSpv; 443 Options |= EOptionLinkProgram; 444 // undo a -H default to Vulkan 445 Options &= ~EOptionVulkanRules; 446 }; 447 448 const auto getUniformOverride = [getStringOperand]() { 449 const char *arg = getStringOperand("-u<name>:<location>"); 450 const char *split = strchr(arg, ':'); 451 if (split == NULL) { 452 printf("%s: missing location\n", arg); 453 exit(EFailUsage); 454 } 455 errno = 0; 456 int location = ::strtol(split + 1, NULL, 10); 457 if (errno) { 458 printf("%s: invalid location\n", arg); 459 exit(EFailUsage); 460 } 461 return std::make_pair(std::string(arg, split - arg), location); 462 }; 463 464 for (bumpArg(); argc >= 1; bumpArg()) { 465 if (argv[0][0] == '-') { 466 switch (argv[0][1]) { 467 case '-': 468 { 469 std::string lowerword(argv[0]+2); 470 std::transform(lowerword.begin(), lowerword.end(), lowerword.begin(), ::tolower); 471 472 // handle --word style options 473 if (lowerword == "auto-map-bindings" || // synonyms 474 lowerword == "auto-map-binding" || 475 lowerword == "amb") { 476 Options |= EOptionAutoMapBindings; 477 } else if (lowerword == "auto-map-locations" || // synonyms 478 lowerword == "aml") { 479 Options |= EOptionAutoMapLocations; 480 } else if (lowerword == "uniform-base") { 481 if (argc <= 1) 482 Error("no <base> provided for --uniform-base"); 483 uniformBase = ::strtol(argv[1], NULL, 10); 484 bumpArg(); 485 break; 486 } else if (lowerword == "client") { 487 if (argc > 1) { 488 if (strcmp(argv[1], "vulkan100") == 0) 489 setVulkanSpv(); 490 else if (strcmp(argv[1], "opengl100") == 0) 491 setOpenGlSpv(); 492 else 493 Error("--client expects vulkan100 or opengl100"); 494 } 495 bumpArg(); 496 } else if (lowerword == "entry-point") { 497 entryPointName = argv[1]; 498 if (argc <= 1) 499 Error("no <name> provided for --entry-point"); 500 bumpArg(); 501 } else if (lowerword == "flatten-uniform-arrays" || // synonyms 502 lowerword == "flatten-uniform-array" || 503 lowerword == "fua") { 504 Options |= EOptionFlattenUniformArrays; 505 } else if (lowerword == "hlsl-offsets") { 506 Options |= EOptionHlslOffsets; 507 } else if (lowerword == "hlsl-iomap" || 508 lowerword == "hlsl-iomapper" || 509 lowerword == "hlsl-iomapping") { 510 Options |= EOptionHlslIoMapping; 511 } else if (lowerword == "hlsl-enable-16bit-types") { 512 HlslEnable16BitTypes = true; 513 } else if (lowerword == "hlsl-dx9-compatible") { 514 HlslDX9compatible = true; 515 } else if (lowerword == "invert-y" || // synonyms 516 lowerword == "iy") { 517 Options |= EOptionInvertY; 518 } else if (lowerword == "keep-uncalled" || // synonyms 519 lowerword == "ku") { 520 Options |= EOptionKeepUncalled; 521 } else if (lowerword == "no-storage-format" || // synonyms 522 lowerword == "nsf") { 523 Options |= EOptionNoStorageFormat; 524 } else if (lowerword == "relaxed-errors") { 525 Options |= EOptionRelaxedErrors; 526 } else if (lowerword == "resource-set-bindings" || // synonyms 527 lowerword == "resource-set-binding" || 528 lowerword == "rsb") { 529 ProcessResourceSetBindingBase(argc, argv, baseResourceSetBinding); 530 } else if (lowerword == "shift-image-bindings" || // synonyms 531 lowerword == "shift-image-binding" || 532 lowerword == "sib") { 533 ProcessBindingBase(argc, argv, glslang::EResImage); 534 } else if (lowerword == "shift-sampler-bindings" || // synonyms 535 lowerword == "shift-sampler-binding" || 536 lowerword == "ssb") { 537 ProcessBindingBase(argc, argv, glslang::EResSampler); 538 } else if (lowerword == "shift-uav-bindings" || // synonyms 539 lowerword == "shift-uav-binding" || 540 lowerword == "suavb") { 541 ProcessBindingBase(argc, argv, glslang::EResUav); 542 } else if (lowerword == "shift-texture-bindings" || // synonyms 543 lowerword == "shift-texture-binding" || 544 lowerword == "stb") { 545 ProcessBindingBase(argc, argv, glslang::EResTexture); 546 } else if (lowerword == "shift-ubo-bindings" || // synonyms 547 lowerword == "shift-ubo-binding" || 548 lowerword == "shift-cbuffer-bindings" || 549 lowerword == "shift-cbuffer-binding" || 550 lowerword == "sub" || 551 lowerword == "scb") { 552 ProcessBindingBase(argc, argv, glslang::EResUbo); 553 } else if (lowerword == "shift-ssbo-bindings" || // synonyms 554 lowerword == "shift-ssbo-binding" || 555 lowerword == "sbb") { 556 ProcessBindingBase(argc, argv, glslang::EResSsbo); 557 } else if (lowerword == "source-entrypoint" || // synonyms 558 lowerword == "sep") { 559 if (argc <= 1) 560 Error("no <entry-point> provided for --source-entrypoint"); 561 sourceEntryPointName = argv[1]; 562 bumpArg(); 563 break; 564 } else if (lowerword == "spirv-dis") { 565 SpvToolsDisassembler = true; 566 } else if (lowerword == "spirv-val") { 567 SpvToolsValidate = true; 568 } else if (lowerword == "stdin") { 569 Options |= EOptionStdin; 570 shaderStageName = argv[1]; 571 } else if (lowerword == "suppress-warnings") { 572 Options |= EOptionSuppressWarnings; 573 } else if (lowerword == "target-env") { 574 if (argc > 1) { 575 if (strcmp(argv[1], "vulkan1.0") == 0) { 576 setVulkanSpv(); 577 ClientVersion = glslang::EShTargetVulkan_1_0; 578 } else if (strcmp(argv[1], "vulkan1.1") == 0) { 579 setVulkanSpv(); 580 ClientVersion = glslang::EShTargetVulkan_1_1; 581 } else if (strcmp(argv[1], "opengl") == 0) { 582 setOpenGlSpv(); 583 ClientVersion = glslang::EShTargetOpenGL_450; 584 } else if (strcmp(argv[1], "spirv1.0") == 0) { 585 TargetLanguage = glslang::EShTargetSpv; 586 TargetVersion = glslang::EShTargetSpv_1_0; 587 } else if (strcmp(argv[1], "spirv1.1") == 0) { 588 TargetLanguage = glslang::EShTargetSpv; 589 TargetVersion = glslang::EShTargetSpv_1_1; 590 } else if (strcmp(argv[1], "spirv1.2") == 0) { 591 TargetLanguage = glslang::EShTargetSpv; 592 TargetVersion = glslang::EShTargetSpv_1_2; 593 } else if (strcmp(argv[1], "spirv1.3") == 0) { 594 TargetLanguage = glslang::EShTargetSpv; 595 TargetVersion = glslang::EShTargetSpv_1_3; 596 } else if (strcmp(argv[1], "spirv1.4") == 0) { 597 TargetLanguage = glslang::EShTargetSpv; 598 TargetVersion = glslang::EShTargetSpv_1_4; 599 } else 600 Error("--target-env expected one of: vulkan1.0, vulkan1.1, opengl, spirv1.0, spirv1.1, spirv1.2, or spirv1.3"); 601 } 602 bumpArg(); 603 } else if (lowerword == "variable-name" || // synonyms 604 lowerword == "vn") { 605 Options |= EOptionOutputHexadecimal; 606 if (argc <= 1) 607 Error("no <C-variable-name> provided for --variable-name"); 608 variableName = argv[1]; 609 bumpArg(); 610 break; 611 } else if (lowerword == "version") { 612 Options |= EOptionDumpVersions; 613 } else { 614 usage(); 615 } 616 } 617 break; 618 case 'C': 619 Options |= EOptionCascadingErrors; 620 break; 621 case 'D': 622 if (argv[0][2] == 0) 623 Options |= EOptionReadHlsl; 624 else 625 UserPreamble.addDef(getStringOperand("-D<macro> macro name")); 626 break; 627 case 'u': 628 uniformLocationOverrides.push_back(getUniformOverride()); 629 break; 630 case 'E': 631 Options |= EOptionOutputPreprocessed; 632 break; 633 case 'G': 634 // OpenGL client 635 setOpenGlSpv(); 636 if (argv[0][2] != 0) 637 ClientInputSemanticsVersion = getAttachedNumber("-G<num> client input semantics"); 638 break; 639 case 'H': 640 Options |= EOptionHumanReadableSpv; 641 if ((Options & EOptionSpv) == 0) { 642 // default to Vulkan 643 setVulkanSpv(); 644 } 645 break; 646 case 'I': 647 IncludeDirectoryList.push_back(getStringOperand("-I<dir> include path")); 648 break; 649 case 'O': 650 if (argv[0][2] == 'd') 651 Options |= EOptionOptimizeDisable; 652 else if (argv[0][2] == 's') 653 #if ENABLE_OPT 654 Options |= EOptionOptimizeSize; 655 #else 656 Error("-Os not available; optimizer not linked"); 657 #endif 658 else 659 Error("unknown -O option"); 660 break; 661 case 'S': 662 if (argc <= 1) 663 Error("no <stage> specified for -S"); 664 shaderStageName = argv[1]; 665 bumpArg(); 666 break; 667 case 'U': 668 UserPreamble.addUndef(getStringOperand("-U<macro>: macro name")); 669 break; 670 case 'V': 671 setVulkanSpv(); 672 if (argv[0][2] != 0) 673 ClientInputSemanticsVersion = getAttachedNumber("-V<num> client input semantics"); 674 break; 675 case 'c': 676 Options |= EOptionDumpConfig; 677 break; 678 case 'd': 679 if (strncmp(&argv[0][1], "dumpversion", strlen(&argv[0][1]) + 1) == 0 || 680 strncmp(&argv[0][1], "dumpfullversion", strlen(&argv[0][1]) + 1) == 0) 681 Options |= EOptionDumpBareVersion; 682 else 683 Options |= EOptionDefaultDesktop; 684 break; 685 case 'e': 686 entryPointName = argv[1]; 687 if (argc <= 1) 688 Error("no <name> provided for -e"); 689 bumpArg(); 690 break; 691 case 'f': 692 if (strcmp(&argv[0][2], "hlsl_functionality1") == 0) 693 targetHlslFunctionality1 = true; 694 else 695 Error("-f: expected hlsl_functionality1"); 696 break; 697 case 'g': 698 Options |= EOptionDebug; 699 break; 700 case 'h': 701 usage(); 702 break; 703 case 'i': 704 Options |= EOptionIntermediate; 705 break; 706 case 'l': 707 Options |= EOptionLinkProgram; 708 break; 709 case 'm': 710 Options |= EOptionMemoryLeakMode; 711 break; 712 case 'o': 713 if (argc <= 1) 714 Error("no <file> provided for -o"); 715 binaryFileName = argv[1]; 716 bumpArg(); 717 break; 718 case 'q': 719 Options |= EOptionDumpReflection; 720 break; 721 case 'r': 722 Options |= EOptionRelaxedErrors; 723 break; 724 case 's': 725 Options |= EOptionSuppressInfolog; 726 break; 727 case 't': 728 Options |= EOptionMultiThreaded; 729 break; 730 case 'v': 731 Options |= EOptionDumpVersions; 732 break; 733 case 'w': 734 Options |= EOptionSuppressWarnings; 735 break; 736 case 'x': 737 Options |= EOptionOutputHexadecimal; 738 break; 739 default: 740 usage(); 741 break; 742 } 743 } else { 744 std::string name(argv[0]); 745 if (! SetConfigFile(name)) { 746 workItems.push_back(std::unique_ptr<glslang::TWorkItem>(new glslang::TWorkItem(name))); 747 } 748 } 749 } 750 751 // Make sure that -S is always specified if --stdin is specified 752 if ((Options & EOptionStdin) && shaderStageName == nullptr) 753 Error("must provide -S when --stdin is given"); 754 755 // Make sure that -E is not specified alongside linking (which includes SPV generation) 756 if ((Options & EOptionOutputPreprocessed) && (Options & EOptionLinkProgram)) 757 Error("can't use -E when linking is selected"); 758 759 // -o or -x makes no sense if there is no target binary 760 if (binaryFileName && (Options & EOptionSpv) == 0) 761 Error("no binary generation requested (e.g., -V)"); 762 763 if ((Options & EOptionFlattenUniformArrays) != 0 && 764 (Options & EOptionReadHlsl) == 0) 765 Error("uniform array flattening only valid when compiling HLSL source."); 766 767 // rationalize client and target language 768 if (TargetLanguage == glslang::EShTargetNone) { 769 switch (ClientVersion) { 770 case glslang::EShTargetVulkan_1_0: 771 TargetLanguage = glslang::EShTargetSpv; 772 TargetVersion = glslang::EShTargetSpv_1_0; 773 break; 774 case glslang::EShTargetVulkan_1_1: 775 TargetLanguage = glslang::EShTargetSpv; 776 TargetVersion = glslang::EShTargetSpv_1_3; 777 break; 778 case glslang::EShTargetOpenGL_450: 779 TargetLanguage = glslang::EShTargetSpv; 780 TargetVersion = glslang::EShTargetSpv_1_0; 781 break; 782 default: 783 break; 784 } 785 } 786 if (TargetLanguage != glslang::EShTargetNone && Client == glslang::EShClientNone) 787 Error("To generate SPIR-V, also specify client semantics. See -G and -V."); 788 } 789 790 // 791 // Translate the meaningful subset of command-line options to parser-behavior options. 792 // 793 void SetMessageOptions(EShMessages& messages) 794 { 795 if (Options & EOptionRelaxedErrors) 796 messages = (EShMessages)(messages | EShMsgRelaxedErrors); 797 if (Options & EOptionIntermediate) 798 messages = (EShMessages)(messages | EShMsgAST); 799 if (Options & EOptionSuppressWarnings) 800 messages = (EShMessages)(messages | EShMsgSuppressWarnings); 801 if (Options & EOptionSpv) 802 messages = (EShMessages)(messages | EShMsgSpvRules); 803 if (Options & EOptionVulkanRules) 804 messages = (EShMessages)(messages | EShMsgVulkanRules); 805 if (Options & EOptionOutputPreprocessed) 806 messages = (EShMessages)(messages | EShMsgOnlyPreprocessor); 807 if (Options & EOptionReadHlsl) 808 messages = (EShMessages)(messages | EShMsgReadHlsl); 809 if (Options & EOptionCascadingErrors) 810 messages = (EShMessages)(messages | EShMsgCascadingErrors); 811 if (Options & EOptionKeepUncalled) 812 messages = (EShMessages)(messages | EShMsgKeepUncalled); 813 if (Options & EOptionHlslOffsets) 814 messages = (EShMessages)(messages | EShMsgHlslOffsets); 815 if (Options & EOptionDebug) 816 messages = (EShMessages)(messages | EShMsgDebugInfo); 817 if (HlslEnable16BitTypes) 818 messages = (EShMessages)(messages | EShMsgHlslEnable16BitTypes); 819 if ((Options & EOptionOptimizeDisable) || !ENABLE_OPT) 820 messages = (EShMessages)(messages | EShMsgHlslLegalization); 821 if (HlslDX9compatible) 822 messages = (EShMessages)(messages | EShMsgHlslDX9Compatible); 823 } 824 825 // 826 // Thread entry point, for non-linking asynchronous mode. 827 // 828 void CompileShaders(glslang::TWorklist& worklist) 829 { 830 if (Options & EOptionDebug) 831 Error("cannot generate debug information unless linking to generate code"); 832 833 glslang::TWorkItem* workItem; 834 if (Options & EOptionStdin) { 835 if (worklist.remove(workItem)) { 836 ShHandle compiler = ShConstructCompiler(FindLanguage("stdin"), Options); 837 if (compiler == nullptr) 838 return; 839 840 CompileFile("stdin", compiler); 841 842 if (! (Options & EOptionSuppressInfolog)) 843 workItem->results = ShGetInfoLog(compiler); 844 845 ShDestruct(compiler); 846 } 847 } else { 848 while (worklist.remove(workItem)) { 849 ShHandle compiler = ShConstructCompiler(FindLanguage(workItem->name), Options); 850 if (compiler == 0) 851 return; 852 853 CompileFile(workItem->name.c_str(), compiler); 854 855 if (! (Options & EOptionSuppressInfolog)) 856 workItem->results = ShGetInfoLog(compiler); 857 858 ShDestruct(compiler); 859 } 860 } 861 } 862 863 // Outputs the given string, but only if it is non-null and non-empty. 864 // This prevents erroneous newlines from appearing. 865 void PutsIfNonEmpty(const char* str) 866 { 867 if (str && str[0]) { 868 puts(str); 869 } 870 } 871 872 // Outputs the given string to stderr, but only if it is non-null and non-empty. 873 // This prevents erroneous newlines from appearing. 874 void StderrIfNonEmpty(const char* str) 875 { 876 if (str && str[0]) 877 fprintf(stderr, "%s\n", str); 878 } 879 880 // Simple bundling of what makes a compilation unit for ease in passing around, 881 // and separation of handling file IO versus API (programmatic) compilation. 882 struct ShaderCompUnit { 883 EShLanguage stage; 884 static const int maxCount = 1; 885 int count; // live number of strings/names 886 const char* text[maxCount]; // memory owned/managed externally 887 std::string fileName[maxCount]; // hold's the memory, but... 888 const char* fileNameList[maxCount]; // downstream interface wants pointers 889 890 ShaderCompUnit(EShLanguage stage) : stage(stage), count(0) { } 891 892 ShaderCompUnit(const ShaderCompUnit& rhs) 893 { 894 stage = rhs.stage; 895 count = rhs.count; 896 for (int i = 0; i < count; ++i) { 897 fileName[i] = rhs.fileName[i]; 898 text[i] = rhs.text[i]; 899 fileNameList[i] = rhs.fileName[i].c_str(); 900 } 901 } 902 903 void addString(std::string& ifileName, const char* itext) 904 { 905 assert(count < maxCount); 906 fileName[count] = ifileName; 907 text[count] = itext; 908 fileNameList[count] = fileName[count].c_str(); 909 ++count; 910 } 911 }; 912 913 // 914 // For linking mode: Will independently parse each compilation unit, but then put them 915 // in the same program and link them together, making at most one linked module per 916 // pipeline stage. 917 // 918 // Uses the new C++ interface instead of the old handle-based interface. 919 // 920 921 void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits) 922 { 923 // keep track of what to free 924 std::list<glslang::TShader*> shaders; 925 926 EShMessages messages = EShMsgDefault; 927 SetMessageOptions(messages); 928 929 // 930 // Per-shader processing... 931 // 932 933 glslang::TProgram& program = *new glslang::TProgram; 934 for (auto it = compUnits.cbegin(); it != compUnits.cend(); ++it) { 935 const auto &compUnit = *it; 936 glslang::TShader* shader = new glslang::TShader(compUnit.stage); 937 shader->setStringsWithLengthsAndNames(compUnit.text, NULL, compUnit.fileNameList, compUnit.count); 938 if (entryPointName) 939 shader->setEntryPoint(entryPointName); 940 if (sourceEntryPointName) { 941 if (entryPointName == nullptr) 942 printf("Warning: Changing source entry point name without setting an entry-point name.\n" 943 "Use '-e <name>'.\n"); 944 shader->setSourceEntryPoint(sourceEntryPointName); 945 } 946 if (UserPreamble.isSet()) 947 shader->setPreamble(UserPreamble.get()); 948 shader->addProcesses(Processes); 949 950 // Set IO mapper binding shift values 951 for (int r = 0; r < glslang::EResCount; ++r) { 952 const glslang::TResourceType res = glslang::TResourceType(r); 953 954 // Set base bindings 955 shader->setShiftBinding(res, baseBinding[res][compUnit.stage]); 956 957 // Set bindings for particular resource sets 958 // TODO: use a range based for loop here, when available in all environments. 959 for (auto i = baseBindingForSet[res][compUnit.stage].begin(); 960 i != baseBindingForSet[res][compUnit.stage].end(); ++i) 961 shader->setShiftBindingForSet(res, i->second, i->first); 962 } 963 964 shader->setFlattenUniformArrays((Options & EOptionFlattenUniformArrays) != 0); 965 shader->setNoStorageFormat((Options & EOptionNoStorageFormat) != 0); 966 shader->setResourceSetBinding(baseResourceSetBinding[compUnit.stage]); 967 968 if (Options & EOptionHlslIoMapping) 969 shader->setHlslIoMapping(true); 970 971 if (Options & EOptionAutoMapBindings) 972 shader->setAutoMapBindings(true); 973 974 if (Options & EOptionAutoMapLocations) 975 shader->setAutoMapLocations(true); 976 977 if (Options & EOptionInvertY) 978 shader->setInvertY(true); 979 980 for (auto& uniOverride : uniformLocationOverrides) { 981 shader->addUniformLocationOverride(uniOverride.first.c_str(), 982 uniOverride.second); 983 } 984 985 shader->setUniformLocationBase(uniformBase); 986 987 // Set up the environment, some subsettings take precedence over earlier 988 // ways of setting things. 989 if (Options & EOptionSpv) { 990 shader->setEnvInput((Options & EOptionReadHlsl) ? glslang::EShSourceHlsl 991 : glslang::EShSourceGlsl, 992 compUnit.stage, Client, ClientInputSemanticsVersion); 993 shader->setEnvClient(Client, ClientVersion); 994 shader->setEnvTarget(TargetLanguage, TargetVersion); 995 if (targetHlslFunctionality1) 996 shader->setEnvTargetHlslFunctionality1(); 997 } 998 999 shaders.push_back(shader); 1000 1001 const int defaultVersion = Options & EOptionDefaultDesktop ? 110 : 100; 1002 1003 DirStackFileIncluder includer; 1004 std::for_each(IncludeDirectoryList.rbegin(), IncludeDirectoryList.rend(), [&includer](const std::string& dir) { 1005 includer.pushExternalLocalDirectory(dir); }); 1006 if (Options & EOptionOutputPreprocessed) { 1007 std::string str; 1008 if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false, messages, &str, includer)) { 1009 PutsIfNonEmpty(str.c_str()); 1010 } else { 1011 CompileFailed = true; 1012 } 1013 StderrIfNonEmpty(shader->getInfoLog()); 1014 StderrIfNonEmpty(shader->getInfoDebugLog()); 1015 continue; 1016 } 1017 1018 if (! shader->parse(&Resources, defaultVersion, false, messages, includer)) 1019 CompileFailed = true; 1020 1021 program.addShader(shader); 1022 1023 if (! (Options & EOptionSuppressInfolog) && 1024 ! (Options & EOptionMemoryLeakMode)) { 1025 PutsIfNonEmpty(compUnit.fileName[0].c_str()); 1026 PutsIfNonEmpty(shader->getInfoLog()); 1027 PutsIfNonEmpty(shader->getInfoDebugLog()); 1028 } 1029 } 1030 1031 // 1032 // Program-level processing... 1033 // 1034 1035 // Link 1036 if (! (Options & EOptionOutputPreprocessed) && ! program.link(messages)) 1037 LinkFailed = true; 1038 1039 // Map IO 1040 if (Options & EOptionSpv) { 1041 if (!program.mapIO()) 1042 LinkFailed = true; 1043 } 1044 1045 // Report 1046 if (! (Options & EOptionSuppressInfolog) && 1047 ! (Options & EOptionMemoryLeakMode)) { 1048 PutsIfNonEmpty(program.getInfoLog()); 1049 PutsIfNonEmpty(program.getInfoDebugLog()); 1050 } 1051 1052 // Reflect 1053 if (Options & EOptionDumpReflection) { 1054 program.buildReflection(); 1055 program.dumpReflection(); 1056 } 1057 1058 // Dump SPIR-V 1059 if (Options & EOptionSpv) { 1060 if (CompileFailed || LinkFailed) 1061 printf("SPIR-V is not generated for failed compile or link\n"); 1062 else { 1063 for (int stage = 0; stage < EShLangCount; ++stage) { 1064 if (program.getIntermediate((EShLanguage)stage)) { 1065 std::vector<unsigned int> spirv; 1066 std::string warningsErrors; 1067 spv::SpvBuildLogger logger; 1068 glslang::SpvOptions spvOptions; 1069 if (Options & EOptionDebug) 1070 spvOptions.generateDebugInfo = true; 1071 spvOptions.disableOptimizer = (Options & EOptionOptimizeDisable) != 0; 1072 spvOptions.optimizeSize = (Options & EOptionOptimizeSize) != 0; 1073 spvOptions.disassemble = SpvToolsDisassembler; 1074 spvOptions.validate = SpvToolsValidate; 1075 glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv, &logger, &spvOptions); 1076 1077 // Dump the spv to a file or stdout, etc., but only if not doing 1078 // memory/perf testing, as it's not internal to programmatic use. 1079 if (! (Options & EOptionMemoryLeakMode)) { 1080 printf("%s", logger.getAllMessages().c_str()); 1081 if (Options & EOptionOutputHexadecimal) { 1082 glslang::OutputSpvHex(spirv, GetBinaryName((EShLanguage)stage), variableName); 1083 } else { 1084 glslang::OutputSpvBin(spirv, GetBinaryName((EShLanguage)stage)); 1085 } 1086 if (!SpvToolsDisassembler && (Options & EOptionHumanReadableSpv)) 1087 spv::Disassemble(std::cout, spirv); 1088 } 1089 } 1090 } 1091 } 1092 } 1093 1094 // Free everything up, program has to go before the shaders 1095 // because it might have merged stuff from the shaders, and 1096 // the stuff from the shaders has to have its destructors called 1097 // before the pools holding the memory in the shaders is freed. 1098 delete &program; 1099 while (shaders.size() > 0) { 1100 delete shaders.back(); 1101 shaders.pop_back(); 1102 } 1103 } 1104 1105 // 1106 // Do file IO part of compile and link, handing off the pure 1107 // API/programmatic mode to CompileAndLinkShaderUnits(), which can 1108 // be put in a loop for testing memory footprint and performance. 1109 // 1110 // This is just for linking mode: meaning all the shaders will be put into the 1111 // the same program linked together. 1112 // 1113 // This means there are a limited number of work items (not multi-threading mode) 1114 // and that the point is testing at the linking level. Hence, to enable 1115 // performance and memory testing, the actual compile/link can be put in 1116 // a loop, independent of processing the work items and file IO. 1117 // 1118 void CompileAndLinkShaderFiles(glslang::TWorklist& Worklist) 1119 { 1120 std::vector<ShaderCompUnit> compUnits; 1121 1122 // If this is using stdin, we can't really detect multiple different file 1123 // units by input type. We need to assume that we're just being given one 1124 // file of a certain type. 1125 if ((Options & EOptionStdin) != 0) { 1126 ShaderCompUnit compUnit(FindLanguage("stdin")); 1127 std::istreambuf_iterator<char> begin(std::cin), end; 1128 std::string tempString(begin, end); 1129 char* fileText = strdup(tempString.c_str()); 1130 std::string fileName = "stdin"; 1131 compUnit.addString(fileName, fileText); 1132 compUnits.push_back(compUnit); 1133 } else { 1134 // Transfer all the work items from to a simple list of 1135 // of compilation units. (We don't care about the thread 1136 // work-item distribution properties in this path, which 1137 // is okay due to the limited number of shaders, know since 1138 // they are all getting linked together.) 1139 glslang::TWorkItem* workItem; 1140 while (Worklist.remove(workItem)) { 1141 ShaderCompUnit compUnit(FindLanguage(workItem->name)); 1142 char* fileText = ReadFileData(workItem->name.c_str()); 1143 if (fileText == nullptr) 1144 usage(); 1145 compUnit.addString(workItem->name, fileText); 1146 compUnits.push_back(compUnit); 1147 } 1148 } 1149 1150 // Actual call to programmatic processing of compile and link, 1151 // in a loop for testing memory and performance. This part contains 1152 // all the perf/memory that a programmatic consumer will care about. 1153 for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { 1154 for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) 1155 CompileAndLinkShaderUnits(compUnits); 1156 1157 if (Options & EOptionMemoryLeakMode) 1158 glslang::OS_DumpMemoryCounters(); 1159 } 1160 1161 // free memory from ReadFileData, which got stored in a const char* 1162 // as the first string above 1163 for (auto it = compUnits.begin(); it != compUnits.end(); ++it) 1164 FreeFileData(const_cast<char*>(it->text[0])); 1165 } 1166 1167 int singleMain() 1168 { 1169 glslang::TWorklist workList; 1170 std::for_each(WorkItems.begin(), WorkItems.end(), [&workList](std::unique_ptr<glslang::TWorkItem>& item) { 1171 assert(item); 1172 workList.add(item.get()); 1173 }); 1174 1175 if (Options & EOptionDumpConfig) { 1176 printf("%s", glslang::GetDefaultTBuiltInResourceString().c_str()); 1177 if (workList.empty()) 1178 return ESuccess; 1179 } 1180 1181 if (Options & EOptionDumpBareVersion) { 1182 printf("%d.%d.%d\n", 1183 glslang::GetSpirvGeneratorVersion(), GLSLANG_MINOR_VERSION, GLSLANG_PATCH_LEVEL); 1184 if (workList.empty()) 1185 return ESuccess; 1186 } else if (Options & EOptionDumpVersions) { 1187 printf("Glslang Version: %d.%d.%d\n", 1188 glslang::GetSpirvGeneratorVersion(), GLSLANG_MINOR_VERSION, GLSLANG_PATCH_LEVEL); 1189 printf("ESSL Version: %s\n", glslang::GetEsslVersionString()); 1190 printf("GLSL Version: %s\n", glslang::GetGlslVersionString()); 1191 std::string spirvVersion; 1192 glslang::GetSpirvVersion(spirvVersion); 1193 printf("SPIR-V Version %s\n", spirvVersion.c_str()); 1194 printf("GLSL.std.450 Version %d, Revision %d\n", GLSLstd450Version, GLSLstd450Revision); 1195 printf("Khronos Tool ID %d\n", glslang::GetKhronosToolId()); 1196 printf("SPIR-V Generator Version %d\n", glslang::GetSpirvGeneratorVersion()); 1197 printf("GL_KHR_vulkan_glsl version %d\n", 100); 1198 printf("ARB_GL_gl_spirv version %d\n", 100); 1199 if (workList.empty()) 1200 return ESuccess; 1201 } 1202 1203 if (workList.empty() && ((Options & EOptionStdin) == 0)) { 1204 usage(); 1205 } 1206 1207 if (Options & EOptionStdin) { 1208 WorkItems.push_back(std::unique_ptr<glslang::TWorkItem>{new glslang::TWorkItem("stdin")}); 1209 workList.add(WorkItems.back().get()); 1210 } 1211 1212 ProcessConfigFile(); 1213 1214 if ((Options & EOptionReadHlsl) && !((Options & EOptionOutputPreprocessed) || (Options & EOptionSpv))) 1215 Error("ERROR: HLSL requires SPIR-V code generation (or preprocessing only)"); 1216 1217 // 1218 // Two modes: 1219 // 1) linking all arguments together, single-threaded, new C++ interface 1220 // 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety, using the old handle interface 1221 // 1222 if (Options & (EOptionLinkProgram | EOptionOutputPreprocessed)) { 1223 glslang::InitializeProcess(); 1224 glslang::InitializeProcess(); // also test reference counting of users 1225 glslang::InitializeProcess(); // also test reference counting of users 1226 glslang::FinalizeProcess(); // also test reference counting of users 1227 glslang::FinalizeProcess(); // also test reference counting of users 1228 CompileAndLinkShaderFiles(workList); 1229 glslang::FinalizeProcess(); 1230 } else { 1231 ShInitialize(); 1232 ShInitialize(); // also test reference counting of users 1233 ShFinalize(); // also test reference counting of users 1234 1235 bool printShaderNames = workList.size() > 1; 1236 1237 if (Options & EOptionMultiThreaded) { 1238 std::array<std::thread, 16> threads; 1239 for (unsigned int t = 0; t < threads.size(); ++t) { 1240 threads[t] = std::thread(CompileShaders, std::ref(workList)); 1241 if (threads[t].get_id() == std::thread::id()) { 1242 fprintf(stderr, "Failed to create thread\n"); 1243 return EFailThreadCreate; 1244 } 1245 } 1246 1247 std::for_each(threads.begin(), threads.end(), [](std::thread& t) { t.join(); }); 1248 } else 1249 CompileShaders(workList); 1250 1251 // Print out all the resulting infologs 1252 for (size_t w = 0; w < WorkItems.size(); ++w) { 1253 if (WorkItems[w]) { 1254 if (printShaderNames || WorkItems[w]->results.size() > 0) 1255 PutsIfNonEmpty(WorkItems[w]->name.c_str()); 1256 PutsIfNonEmpty(WorkItems[w]->results.c_str()); 1257 } 1258 } 1259 1260 ShFinalize(); 1261 } 1262 1263 if (CompileFailed) 1264 return EFailCompile; 1265 if (LinkFailed) 1266 return EFailLink; 1267 1268 return 0; 1269 } 1270 1271 int C_DECL main(int argc, char* argv[]) 1272 { 1273 ProcessArguments(WorkItems, argc, argv); 1274 1275 int ret = 0; 1276 1277 // Loop over the entire init/finalize cycle to watch memory changes 1278 const int iterations = 1; 1279 if (iterations > 1) 1280 glslang::OS_DumpMemoryCounters(); 1281 for (int i = 0; i < iterations; ++i) { 1282 ret = singleMain(); 1283 if (iterations > 1) 1284 glslang::OS_DumpMemoryCounters(); 1285 } 1286 1287 return ret; 1288 } 1289 1290 // 1291 // Deduce the language from the filename. Files must end in one of the 1292 // following extensions: 1293 // 1294 // .vert = vertex 1295 // .tesc = tessellation control 1296 // .tese = tessellation evaluation 1297 // .geom = geometry 1298 // .frag = fragment 1299 // .comp = compute 1300 // .rgen = ray generation 1301 // .rint = ray intersection 1302 // .rahit = ray any hit 1303 // .rchit = ray closest hit 1304 // .rmiss = ray miss 1305 // .rcall = ray callable 1306 // .mesh = mesh 1307 // .task = task 1308 // Additionally, the file names may end in .<stage>.glsl and .<stage>.hlsl 1309 // where <stage> is one of the stages listed above. 1310 // 1311 EShLanguage FindLanguage(const std::string& name, bool parseStageName) 1312 { 1313 std::string stageName; 1314 if (shaderStageName) 1315 stageName = shaderStageName; 1316 else if (parseStageName) { 1317 // Note: "first" extension means "first from the end", i.e. 1318 // if the file is named foo.vert.glsl, then "glsl" is first, 1319 // "vert" is second. 1320 size_t firstExtStart = name.find_last_of("."); 1321 bool hasFirstExt = firstExtStart != std::string::npos; 1322 size_t secondExtStart = hasFirstExt ? name.find_last_of(".", firstExtStart - 1) : std::string::npos; 1323 bool hasSecondExt = secondExtStart != std::string::npos; 1324 std::string firstExt = name.substr(firstExtStart + 1, std::string::npos); 1325 bool usesUnifiedExt = hasFirstExt && (firstExt == "glsl" || firstExt == "hlsl"); 1326 if (usesUnifiedExt && firstExt == "hlsl") 1327 Options |= EOptionReadHlsl; 1328 if (hasFirstExt && !usesUnifiedExt) 1329 stageName = firstExt; 1330 else if (usesUnifiedExt && hasSecondExt) 1331 stageName = name.substr(secondExtStart + 1, firstExtStart - secondExtStart - 1); 1332 else { 1333 usage(); 1334 return EShLangVertex; 1335 } 1336 } else 1337 stageName = name; 1338 1339 if (stageName == "vert") 1340 return EShLangVertex; 1341 else if (stageName == "tesc") 1342 return EShLangTessControl; 1343 else if (stageName == "tese") 1344 return EShLangTessEvaluation; 1345 else if (stageName == "geom") 1346 return EShLangGeometry; 1347 else if (stageName == "frag") 1348 return EShLangFragment; 1349 else if (stageName == "comp") 1350 return EShLangCompute; 1351 #ifdef NV_EXTENSIONS 1352 else if (stageName == "rgen") 1353 return EShLangRayGenNV; 1354 else if (stageName == "rint") 1355 return EShLangIntersectNV; 1356 else if (stageName == "rahit") 1357 return EShLangAnyHitNV; 1358 else if (stageName == "rchit") 1359 return EShLangClosestHitNV; 1360 else if (stageName == "rmiss") 1361 return EShLangMissNV; 1362 else if (stageName == "rcall") 1363 return EShLangCallableNV; 1364 else if (stageName == "mesh") 1365 return EShLangMeshNV; 1366 else if (stageName == "task") 1367 return EShLangTaskNV; 1368 #endif 1369 1370 usage(); 1371 return EShLangVertex; 1372 } 1373 1374 // 1375 // Read a file's data into a string, and compile it using the old interface ShCompile, 1376 // for non-linkable results. 1377 // 1378 void CompileFile(const char* fileName, ShHandle compiler) 1379 { 1380 int ret = 0; 1381 char* shaderString; 1382 if ((Options & EOptionStdin) != 0) { 1383 std::istreambuf_iterator<char> begin(std::cin), end; 1384 std::string tempString(begin, end); 1385 shaderString = strdup(tempString.c_str()); 1386 } else { 1387 shaderString = ReadFileData(fileName); 1388 } 1389 1390 // move to length-based strings, rather than null-terminated strings 1391 int* lengths = new int[1]; 1392 lengths[0] = (int)strlen(shaderString); 1393 1394 EShMessages messages = EShMsgDefault; 1395 SetMessageOptions(messages); 1396 1397 if (UserPreamble.isSet()) 1398 Error("-D and -U options require -l (linking)\n"); 1399 1400 for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { 1401 for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) { 1402 // ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); 1403 ret = ShCompile(compiler, &shaderString, 1, nullptr, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); 1404 // const char* multi[12] = { "# ve", "rsion", " 300 e", "s", "\n#err", 1405 // "or should be l", "ine 1", "string 5\n", "float glo", "bal", 1406 // ";\n#error should be line 2\n void main() {", "global = 2.3;}" }; 1407 // const char* multi[7] = { "/", "/", "\\", "\n", "\n", "#", "version 300 es" }; 1408 // ret = ShCompile(compiler, multi, 7, nullptr, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); 1409 } 1410 1411 if (Options & EOptionMemoryLeakMode) 1412 glslang::OS_DumpMemoryCounters(); 1413 } 1414 1415 delete [] lengths; 1416 FreeFileData(shaderString); 1417 1418 if (ret == 0) 1419 CompileFailed = true; 1420 } 1421 1422 // 1423 // print usage to stdout 1424 // 1425 void usage() 1426 { 1427 printf("Usage: glslangValidator [option]... [file]...\n" 1428 "\n" 1429 "'file' can end in .<stage> for auto-stage classification, where <stage> is:\n" 1430 " .conf to provide a config file that replaces the default configuration\n" 1431 " (see -c option below for generating a template)\n" 1432 " .vert for a vertex shader\n" 1433 " .tesc for a tessellation control shader\n" 1434 " .tese for a tessellation evaluation shader\n" 1435 " .geom for a geometry shader\n" 1436 " .frag for a fragment shader\n" 1437 " .comp for a compute shader\n" 1438 #ifdef NV_EXTENSIONS 1439 " .mesh for a mesh shader\n" 1440 " .task for a task shader\n" 1441 " .rgen for a ray generation shader\n" 1442 " .rint for a ray intersection shader\n" 1443 " .rahit for a ray any hit shader\n" 1444 " .rchit for a ray closest hit shader\n" 1445 " .rmiss for a ray miss shader\n" 1446 " .rcall for a ray callable shader\n" 1447 #endif 1448 " .glsl for .vert.glsl, .tesc.glsl, ..., .comp.glsl compound suffixes\n" 1449 " .hlsl for .vert.hlsl, .tesc.hlsl, ..., .comp.hlsl compound suffixes\n" 1450 "\n" 1451 "Options:\n" 1452 " -C cascading errors; risk crash from accumulation of error recoveries\n" 1453 " -D input is HLSL (this is the default when any suffix is .hlsl)\n" 1454 " -D<macro=def>\n" 1455 " -D<macro> define a pre-processor macro\n" 1456 " -E print pre-processed GLSL; cannot be used with -l;\n" 1457 " errors will appear on stderr\n" 1458 " -G[ver] create SPIR-V binary, under OpenGL semantics; turns on -l;\n" 1459 " default file name is <stage>.spv (-o overrides this);\n" 1460 " 'ver', when present, is the version of the input semantics,\n" 1461 " which will appear in #define GL_SPIRV ver;\n" 1462 " '--client opengl100' is the same as -G100;\n" 1463 " a '--target-env' for OpenGL will also imply '-G'\n" 1464 " -H print human readable form of SPIR-V; turns on -V\n" 1465 " -I<dir> add dir to the include search path; includer's directory\n" 1466 " is searched first, followed by left-to-right order of -I\n" 1467 " -Od disables optimization; may cause illegal SPIR-V for HLSL\n" 1468 " -Os optimizes SPIR-V to minimize size\n" 1469 " -S <stage> uses specified stage rather than parsing the file extension\n" 1470 " choices for <stage> are vert, tesc, tese, geom, frag, or comp\n" 1471 " -U<macro> undefine a pre-processor macro\n" 1472 " -V[ver] create SPIR-V binary, under Vulkan semantics; turns on -l;\n" 1473 " default file name is <stage>.spv (-o overrides this)\n" 1474 " 'ver', when present, is the version of the input semantics,\n" 1475 " which will appear in #define VULKAN ver\n" 1476 " '--client vulkan100' is the same as -V100\n" 1477 " a '--target-env' for Vulkan will also imply '-V'\n" 1478 " -c configuration dump;\n" 1479 " creates the default configuration file (redirect to a .conf file)\n" 1480 " -d default to desktop (#version 110) when there is no shader #version\n" 1481 " (default is ES version 100)\n" 1482 " -e <name> | --entry-point <name>\n" 1483 " specify <name> as the entry-point function name\n" 1484 " -f{hlsl_functionality1}\n" 1485 " 'hlsl_functionality1' enables use of the\n" 1486 " SPV_GOOGLE_hlsl_functionality1 extension\n" 1487 " -g generate debug information\n" 1488 " -h print this usage message\n" 1489 " -i intermediate tree (glslang AST) is printed out\n" 1490 " -l link all input files together to form a single module\n" 1491 " -m memory leak mode\n" 1492 " -o <file> save binary to <file>, requires a binary option (e.g., -V)\n" 1493 " -q dump reflection query database\n" 1494 " -r | --relaxed-errors" 1495 " relaxed GLSL semantic error-checking mode\n" 1496 " -s silence syntax and semantic error reporting\n" 1497 " -t multi-threaded mode\n" 1498 " -v | --version\n" 1499 " print version strings\n" 1500 " -w | --suppress-warnings\n" 1501 " suppress GLSL warnings, except as required by \"#extension : warn\"\n" 1502 " -x save binary output as text-based 32-bit hexadecimal numbers\n" 1503 " -u<name>:<loc> specify a uniform location override for --aml\n" 1504 " --uniform-base <base> set a base to use for generated uniform locations\n" 1505 " --auto-map-bindings | --amb automatically bind uniform variables\n" 1506 " without explicit bindings\n" 1507 " --auto-map-locations | --aml automatically locate input/output lacking\n" 1508 " 'location' (fragile, not cross stage)\n" 1509 " --client {vulkan<ver>|opengl<ver>} see -V and -G\n" 1510 " -dumpfullversion | -dumpversion print bare major.minor.patchlevel\n" 1511 " --flatten-uniform-arrays | --fua flatten uniform texture/sampler arrays to\n" 1512 " scalars\n" 1513 " --hlsl-offsets allow block offsets to follow HLSL rules\n" 1514 " works independently of source language\n" 1515 " --hlsl-iomap perform IO mapping in HLSL register space\n" 1516 " --hlsl-enable-16bit-types allow 16-bit types in SPIR-V for HLSL\n" 1517 " --hlsl-dx9-compatible interprets sampler declarations as a texture/sampler combo like DirectX9 would." 1518 " --invert-y | --iy invert position.Y output in vertex shader\n" 1519 " --keep-uncalled | --ku don't eliminate uncalled functions\n" 1520 " --no-storage-format | --nsf use Unknown image format\n" 1521 " --resource-set-binding [stage] name set binding\n" 1522 " set descriptor set and binding for\n" 1523 " individual resources\n" 1524 " --resource-set-binding [stage] set\n" 1525 " set descriptor set for all resources\n" 1526 " --rsb synonym for --resource-set-binding\n" 1527 " --shift-image-binding [stage] num\n" 1528 " base binding number for images (uav)\n" 1529 " --shift-image-binding [stage] [num set]...\n" 1530 " per-descriptor-set shift values\n" 1531 " --sib synonym for --shift-image-binding\n" 1532 " --shift-sampler-binding [stage] num\n" 1533 " base binding number for samplers\n" 1534 " --shift-sampler-binding [stage] [num set]...\n" 1535 " per-descriptor-set shift values\n" 1536 " --ssb synonym for --shift-sampler-binding\n" 1537 " --shift-ssbo-binding [stage] num base binding number for SSBOs\n" 1538 " --shift-ssbo-binding [stage] [num set]...\n" 1539 " per-descriptor-set shift values\n" 1540 " --sbb synonym for --shift-ssbo-binding\n" 1541 " --shift-texture-binding [stage] num\n" 1542 " base binding number for textures\n" 1543 " --shift-texture-binding [stage] [num set]...\n" 1544 " per-descriptor-set shift values\n" 1545 " --stb synonym for --shift-texture-binding\n" 1546 " --shift-uav-binding [stage] num base binding number for UAVs\n" 1547 " --shift-uav-binding [stage] [num set]...\n" 1548 " per-descriptor-set shift values\n" 1549 " --suavb synonym for --shift-uav-binding\n" 1550 " --shift-UBO-binding [stage] num base binding number for UBOs\n" 1551 " --shift-UBO-binding [stage] [num set]...\n" 1552 " per-descriptor-set shift values\n" 1553 " --sub synonym for --shift-UBO-binding\n" 1554 " --shift-cbuffer-binding | --scb synonyms for --shift-UBO-binding\n" 1555 " --spirv-dis output standard-form disassembly; works only\n" 1556 " when a SPIR-V generation option is also used\n" 1557 " --spirv-val execute the SPIRV-Tools validator\n" 1558 " --source-entrypoint <name> the given shader source function is\n" 1559 " renamed to be the <name> given in -e\n" 1560 " --sep synonym for --source-entrypoint\n" 1561 " --stdin read from stdin instead of from a file;\n" 1562 " requires providing the shader stage using -S\n" 1563 " --target-env {vulkan1.0 | vulkan1.1 | opengl | \n" 1564 " spirv1.0 | spirv1.1 | spirv1.2 | spirv1.3}\n" 1565 " set execution environment that emitted code\n" 1566 " will execute in (versus source language\n" 1567 " semantics selected by --client) defaults:\n" 1568 " * 'vulkan1.0' under '--client vulkan<ver>'\n" 1569 " * 'opengl' under '--client opengl<ver>'\n" 1570 " * 'spirv1.0' under --target-env vulkan1.0\n" 1571 " * 'spirv1.3' under --target-env vulkan1.1\n" 1572 " multiple --targen-env can be specified.\n" 1573 " --variable-name <name>\n" 1574 " --vn <name> creates a C header file that contains a\n" 1575 " uint32_t array named <name>\n" 1576 " initialized with the shader binary code\n" 1577 ); 1578 1579 exit(EFailUsage); 1580 } 1581 1582 #if !defined _MSC_VER && !defined MINGW_HAS_SECURE_API 1583 1584 #include <errno.h> 1585 1586 int fopen_s( 1587 FILE** pFile, 1588 const char* filename, 1589 const char* mode 1590 ) 1591 { 1592 if (!pFile || !filename || !mode) { 1593 return EINVAL; 1594 } 1595 1596 FILE* f = fopen(filename, mode); 1597 if (! f) { 1598 if (errno != 0) { 1599 return errno; 1600 } else { 1601 return ENOENT; 1602 } 1603 } 1604 *pFile = f; 1605 1606 return 0; 1607 } 1608 1609 #endif 1610 1611 // 1612 // Malloc a string of sufficient size and read a string into it. 1613 // 1614 char* ReadFileData(const char* fileName) 1615 { 1616 FILE *in = nullptr; 1617 int errorCode = fopen_s(&in, fileName, "r"); 1618 if (errorCode || in == nullptr) 1619 Error("unable to open input file"); 1620 1621 int count = 0; 1622 while (fgetc(in) != EOF) 1623 count++; 1624 1625 fseek(in, 0, SEEK_SET); 1626 1627 char* return_data = (char*)malloc(count + 1); // freed in FreeFileData() 1628 if ((int)fread(return_data, 1, count, in) != count) { 1629 free(return_data); 1630 Error("can't read input file"); 1631 } 1632 1633 return_data[count] = '\0'; 1634 fclose(in); 1635 1636 return return_data; 1637 } 1638 1639 void FreeFileData(char* data) 1640 { 1641 free(data); 1642 } 1643 1644 void InfoLogMsg(const char* msg, const char* name, const int num) 1645 { 1646 if (num >= 0 ) 1647 printf("#### %s %s %d INFO LOG ####\n", msg, name, num); 1648 else 1649 printf("#### %s %s INFO LOG ####\n", msg, name); 1650 } 1651