// // Thread entry point, for non-linking asynchronous mode. // // Return 0 for failure, 1 for success. // unsigned int CompileShaders(void*) { glslang::TWorkItem* workItem; while (Worklist.remove(workItem)) { ShHandle compiler = ShConstructCompiler(FindLanguage(workItem->name), Options); if (compiler == 0) return 0; CompileFile(workItem->name.c_str(), compiler); if (! (Options & EOptionSuppressInfolog)) workItem->results = ShGetInfoLog(compiler); ShDestruct(compiler); } return 0; }
// // Do file IO part of compile and link, handing off the pure // API/programmatic mode to CompileAndLinkShaderUnits(), which can // be put in a loop for testing memory footprint and performance. // // This is just for linking mode: meaning all the shaders will be put into the // the same program linked together. // // This means there are a limited number of work items (not multi-threading mode) // and that the point is testing at the linking level. Hence, to enable // performance and memory testing, the actual compile/link can be put in // a loop, independent of processing the work items and file IO. // void CompileAndLinkShaderFiles() { std::vector<ShaderCompUnit> compUnits; // Transfer all the work items from to a simple list of // of compilation units. (We don't care about the thread // work-item distribution properties in this path, which // is okay due to the limited number of shaders, know since // they are all getting linked together.) glslang::TWorkItem* workItem; while (Worklist.remove(workItem)) { ShaderCompUnit compUnit( FindLanguage(workItem->name), workItem->name, ReadFileData(workItem->name.c_str()) ); if (! compUnit.text) { usage(); return; } compUnits.push_back(compUnit); } // Actual call to programmatic processing of compile and link, // in a loop for testing memory and performance. This part contains // all the perf/memory that a programmatic consumer will care about. for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) CompileAndLinkShaderUnits(compUnits); if (Options & EOptionMemoryLeakMode) glslang::OS_DumpMemoryCounters(); } for (auto it = compUnits.begin(); it != compUnits.end(); ++it) FreeFileData(it->text); }
int C_DECL main(int argc, char* argv[]) { ProcessArguments(argc, argv); if (Options & EOptionDumpConfig) { printf("%s", DefaultConfig); if (Worklist.empty()) return ESuccess; } if (Options & EOptionDumpVersions) { printf("Glslang Version: %s %s\n", GLSLANG_REVISION, GLSLANG_DATE); printf("ESSL Version: %s\n", glslang::GetEsslVersionString()); printf("GLSL Version: %s\n", glslang::GetGlslVersionString()); std::string spirvVersion; glslang::GetSpirvVersion(spirvVersion); printf("SPIR-V Version %s\n", spirvVersion.c_str()); printf("GLSL.std.450 Version %d, Revision %d\n", GLSLstd450Version, GLSLstd450Revision); if (Worklist.empty()) return ESuccess; } if (Worklist.empty()) { usage(); } ProcessConfigFile(); // // Two modes: // 1) linking all arguments together, single-threaded, new C++ interface // 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety, using the old handle interface // if (Options & EOptionLinkProgram || Options & EOptionOutputPreprocessed) { glslang::InitializeProcess(); CompileAndLinkShaders(); glslang::FinalizeProcess(); } else { ShInitialize(); bool printShaderNames = Worklist.size() > 1; if (Options & EOptionMultiThreaded) { const int NumThreads = 16; void* threads[NumThreads]; for (int t = 0; t < NumThreads; ++t) { threads[t] = glslang::OS_CreateThread(&CompileShaders); if (! threads[t]) { printf("Failed to create thread\n"); return EFailThreadCreate; } } glslang::OS_WaitForAllThreads(threads, NumThreads); } else CompileShaders(0); // Print out all the resulting infologs for (int w = 0; w < NumWorkItems; ++w) { if (Work[w]) { if (printShaderNames) PutsIfNonEmpty(Work[w]->name.c_str()); PutsIfNonEmpty(Work[w]->results.c_str()); delete Work[w]; } } ShFinalize(); } if (CompileFailed) return EFailCompile; if (LinkFailed) return EFailLink; return 0; }
// // For linking mode: Will independently parse each item in the worklist, but then put them // in the same program and link them together. // // Uses the new C++ interface instead of the old handle-based interface. // void CompileAndLinkShaders() { // keep track of what to free std::list<glslang::TShader*> shaders; EShMessages messages = EShMsgDefault; SetMessageOptions(messages); // // Per-shader processing... // glslang::TProgram& program = *new glslang::TProgram; glslang::TWorkItem* workItem; while (Worklist.remove(workItem)) { EShLanguage stage = FindLanguage(workItem->name); glslang::TShader* shader = new glslang::TShader(stage); shaders.push_back(shader); char** shaderStrings = ReadFileData(workItem->name.c_str()); if (! shaderStrings) { usage(); delete &program; return; } const int defaultVersion = Options & EOptionDefaultDesktop? 110: 100; shader->setStrings(shaderStrings, 1); if (Options & EOptionOutputPreprocessed) { std::string str; if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false, messages, &str, glslang::TShader::ForbidInclude())) { PutsIfNonEmpty(str.c_str()); } else { CompileFailed = true; } StderrIfNonEmpty(shader->getInfoLog()); StderrIfNonEmpty(shader->getInfoDebugLog()); FreeFileData(shaderStrings); continue; } if (! shader->parse(&Resources, defaultVersion, false, messages)) CompileFailed = true; program.addShader(shader); if (! (Options & EOptionSuppressInfolog)) { PutsIfNonEmpty(workItem->name.c_str()); PutsIfNonEmpty(shader->getInfoLog()); PutsIfNonEmpty(shader->getInfoDebugLog()); } FreeFileData(shaderStrings); } // // Program-level processing... // if (! (Options & EOptionOutputPreprocessed) && ! program.link(messages)) LinkFailed = true; if (! (Options & EOptionSuppressInfolog)) { PutsIfNonEmpty(program.getInfoLog()); PutsIfNonEmpty(program.getInfoDebugLog()); } if (Options & EOptionDumpReflection) { program.buildReflection(); program.dumpReflection(); } if (Options & EOptionSpv) { if (CompileFailed || LinkFailed) printf("SPIR-V is not generated for failed compile or link\n"); else { for (int stage = 0; stage < EShLangCount; ++stage) { if (program.getIntermediate((EShLanguage)stage)) { std::vector<unsigned int> spirv; glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv); glslang::OutputSpv(spirv, GetBinaryName((EShLanguage)stage)); if (Options & EOptionHumanReadableSpv) { spv::Parameterize(); spv::Disassemble(std::cout, spirv); } } } } } // Free everything up, program has to go before the shaders // because it might have merged stuff from the shaders, and // the stuff from the shaders has to have its destructors called // before the pools holding the memory in the shaders is freed. delete &program; while (shaders.size() > 0) { delete shaders.back(); shaders.pop_back(); } }
// // Do all command-line argument parsing. This includes building up the work-items // to be processed later, and saving all the command-line options. // // Does not return (it exits) if command-line is fatally flawed. // void ProcessArguments(int argc, char* argv[]) { ExecutableName = argv[0]; NumWorkItems = argc; // will include some empties where the '-' options were, but it doesn't matter, they'll be 0 Work = new glslang::TWorkItem*[NumWorkItems]; for (int w = 0; w < NumWorkItems; ++w) Work[w] = 0; argc--; argv++; for (; argc >= 1; argc--, argv++) { if (argv[0][0] == '-') { switch (argv[0][1]) { case 'H': Options |= EOptionHumanReadableSpv; // fall through to -V case 'V': Options |= EOptionSpv; Options |= EOptionVulkanRules; Options |= EOptionLinkProgram; break; case 'G': Options |= EOptionSpv; Options |= EOptionLinkProgram; break; case 'E': Options |= EOptionOutputPreprocessed; break; case 'c': Options |= EOptionDumpConfig; break; case 'd': Options |= EOptionDefaultDesktop; break; case 'h': usage(); break; case 'i': Options |= EOptionIntermediate; break; case 'l': Options |= EOptionLinkProgram; break; case 'm': Options |= EOptionMemoryLeakMode; break; case 'o': binaryFileName = argv[1]; if (argc > 0) { argc--; argv++; } else Error("no <file> provided for -o"); break; case 'q': Options |= EOptionDumpReflection; break; case 'r': Options |= EOptionRelaxedErrors; break; case 's': Options |= EOptionSuppressInfolog; break; case 't': #ifdef _WIN32 Options |= EOptionMultiThreaded; #endif break; case 'v': Options |= EOptionDumpVersions; break; case 'w': Options |= EOptionSuppressWarnings; break; default: usage(); break; } } else { std::string name(argv[0]); if (! SetConfigFile(name)) { Work[argc] = new glslang::TWorkItem(name); Worklist.add(Work[argc]); } } } // Make sure that -E is not specified alongside linking (which includes SPV generation) if ((Options & EOptionOutputPreprocessed) && (Options & EOptionLinkProgram)) Error("can't use -E when linking is selected"); // -o makes no sense if there is no target binary if (binaryFileName && (Options & EOptionSpv) == 0) Error("no binary generation requested (e.g., -V)"); }
int C_DECL main(int argc, char* argv[]) { if (argc < 6) { usage(); return 1; } const char* tempdir = argv[4]; //Options |= EOptionHumanReadableSpv; Options |= EOptionSpv; Options |= EOptionLinkProgram; //Options |= EOptionSuppressInfolog; NumWorkItems = 1; Work = new glslang::TWorkItem*[NumWorkItems]; Work[0] = 0; std::string name(argv[2]); if (!SetConfigFile(name)) { Work[0] = new glslang::TWorkItem(name); Worklist.add(Work[0]); } ProcessConfigFile(); glslang::InitializeProcess(); KrafixIncluder includer(name); krafix::Target target; target.system = getSystem(argv[5]); target.es = false; if (strcmp(argv[1], "d3d9") == 0) { target.lang = krafix::HLSL; target.version = 9; CompileAndLinkShaders(target, argv[3], tempdir, includer); } else if (strcmp(argv[1], "d3d11") == 0) { target.lang = krafix::HLSL; target.version = 11; CompileAndLinkShaders(target, argv[3], tempdir, includer); } else if (strcmp(argv[1], "glsl") == 0) { target.lang = krafix::GLSL; if (target.system == krafix::Linux) target.version = 100; else target.version = 330; CompileAndLinkShaders(target, argv[3], tempdir, includer); } else if (strcmp(argv[1], "essl") == 0) { target.lang = krafix::GLSL; target.version = 100; target.es = true; CompileAndLinkShaders(target, argv[3], tempdir, includer); } else if (strcmp(argv[1], "agal") == 0) { target.lang = krafix::AGAL; target.version = 100; target.es = true; CompileAndLinkShaders(target, argv[3], tempdir, includer); } else if (strcmp(argv[1], "metal") == 0) { target.lang = krafix::Metal; target.version = 1; CompileAndLinkShaders(target, argv[3], tempdir, includer); } else if (strcmp(argv[1], "varlist") == 0) { target.lang = krafix::VarList; target.version = 1; CompileAndLinkShaders(target, argv[3], tempdir, includer); } else { std::cout << "Unknown profile " << argv[1] << std::endl; CompileFailed = true; } glslang::FinalizeProcess(); if (CompileFailed) return EFailCompile; if (LinkFailed) return EFailLink; return 0; }
// // For linking mode: Will independently parse each item in the worklist, but then put them // in the same program and link them together. // // Uses the new C++ interface instead of the old handle-based interface. // void CompileAndLinkShaders(krafix::Target target, const char* filename, const char* tempdir, const glslang::TShader::Includer& includer) { // keep track of what to free std::list<glslang::TShader*> shaders; EShMessages messages = EShMsgDefault; SetMessageOptions(messages); // // Per-shader processing... // glslang::TProgram& program = *new glslang::TProgram; glslang::TWorkItem* workItem; while (Worklist.remove(workItem)) { EShLanguage stage = FindLanguage(workItem->name); glslang::TShader* shader = new glslang::TShader(stage); shaders.push_back(shader); char** shaderStrings = ReadFileData(workItem->name.c_str()); if (! shaderStrings) { usage(); delete &program; return; } const int defaultVersion = Options & EOptionDefaultDesktop? 110: 100; shader->setStrings(shaderStrings, 1); if (Options & EOptionOutputPreprocessed) { std::string str; if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false, messages, &str, includer)) { PutsIfNonEmpty(str.c_str()); } else { CompileFailed = true; } StderrIfNonEmpty(shader->getInfoLog()); StderrIfNonEmpty(shader->getInfoDebugLog()); FreeFileData(shaderStrings); continue; } if (! shader->parse(&Resources, defaultVersion, ENoProfile, false, false, messages, includer)) CompileFailed = true; program.addShader(shader); if (! (Options & EOptionSuppressInfolog)) { //PutsIfNonEmpty(workItem->name.c_str()); PutsIfNonEmpty(shader->getInfoLog()); PutsIfNonEmpty(shader->getInfoDebugLog()); } FreeFileData(shaderStrings); } // // Program-level processing... // if (! (Options & EOptionOutputPreprocessed) && ! program.link(messages)) LinkFailed = true; if (! (Options & EOptionSuppressInfolog)) { PutsIfNonEmpty(program.getInfoLog()); PutsIfNonEmpty(program.getInfoDebugLog()); } if (Options & EOptionDumpReflection) { program.buildReflection(); program.dumpReflection(); } if (Options & EOptionSpv) { if (CompileFailed || LinkFailed) printf("SPIRV is not generated for failed compile or link\n"); else { for (int stage = 0; stage < EShLangCount; ++stage) { if (program.getIntermediate((EShLanguage)stage)) { std::vector<unsigned int> spirv; glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv); krafix::Translator* translator = NULL; std::map<std::string, int> attributes; switch (target.lang) { case krafix::GLSL: translator = new krafix::GlslTranslator(spirv, (EShLanguage)stage); break; case krafix::HLSL: translator = new krafix::HlslTranslator(spirv, (EShLanguage)stage); break; case krafix::Metal: translator = new krafix::MetalTranslator(spirv, (EShLanguage)stage); break; case krafix::AGAL: translator = new krafix::AgalTranslator(spirv, (EShLanguage)stage); break; case krafix::VarList: translator = new krafix::VarListTranslator(spirv, (EShLanguage)stage); break; } if (target.lang == krafix::HLSL && target.system != krafix::Unity) { std::string temp = std::string(tempdir) + "/" + removeExtension(extractFilename(workItem->name)) + ".hlsl"; translator->outputCode(target, temp.c_str(), attributes); if (target.version == 9) { compileHLSLToD3D9(temp.c_str(), filename, attributes, (EShLanguage)stage); } else { compileHLSLToD3D11(temp.c_str(), filename, attributes, (EShLanguage)stage); } } else { translator->outputCode(target, filename, attributes); } delete translator; //glslang::OutputSpv(spirv, GetBinaryName((EShLanguage)stage)); if (Options & EOptionHumanReadableSpv) { spv::Parameterize(); spv::Disassemble(std::cout, spirv); } } } } } // Free everything up, program has to go before the shaders // because it might have merged stuff from the shaders, and // the stuff from the shaders has to have its destructors called // before the pools holding the memory in the shaders is freed. delete &program; while (shaders.size() > 0) { delete shaders.back(); shaders.pop_back(); } }
// // Do all command-line argument parsing. This includes building up the work-items // to be processed later, and saving all the command-line options. // // Does not return (it exits) if command-line is fatally flawed. // void ProcessArguments(int argc, char* argv[]) { baseSamplerBinding.fill(0); baseTextureBinding.fill(0); baseImageBinding.fill(0); baseUboBinding.fill(0); ExecutableName = argv[0]; NumWorkItems = argc; // will include some empties where the '-' options were, but it doesn't matter, they'll be 0 Work = new glslang::TWorkItem*[NumWorkItems]; for (int w = 0; w < NumWorkItems; ++w) Work[w] = 0; argc--; argv++; for (; argc >= 1; argc--, argv++) { if (argv[0][0] == '-') { switch (argv[0][1]) { case '-': { std::string lowerword(argv[0]+2); std::transform(lowerword.begin(), lowerword.end(), lowerword.begin(), ::tolower); // handle --word style options if (lowerword == "shift-sampler-bindings" || // synonyms lowerword == "shift-sampler-binding" || lowerword == "ssb") { ProcessBindingBase(argc, argv, baseSamplerBinding); } else if (lowerword == "shift-texture-bindings" || // synonyms lowerword == "shift-texture-binding" || lowerword == "stb") { ProcessBindingBase(argc, argv, baseTextureBinding); } else if (lowerword == "shift-image-bindings" || // synonyms lowerword == "shift-image-binding" || lowerword == "sib") { ProcessBindingBase(argc, argv, baseImageBinding); } else if (lowerword == "shift-ubo-bindings" || // synonyms lowerword == "shift-ubo-binding" || lowerword == "sub") { ProcessBindingBase(argc, argv, baseUboBinding); } else if (lowerword == "auto-map-bindings" || // synonyms lowerword == "auto-map-binding" || lowerword == "amb") { Options |= EOptionAutoMapBindings; } else if (lowerword == "flatten-uniform-arrays" || // synonyms lowerword == "flatten-uniform-array" || lowerword == "fua") { Options |= EOptionFlattenUniformArrays; } else if (lowerword == "no-storage-format" || // synonyms lowerword == "nsf") { Options |= EOptionNoStorageFormat; } else if (lowerword == "source-entrypoint" || // synonyms lowerword == "sep") { sourceEntryPointName = argv[1]; if (argc > 0) { argc--; argv++; } else Error("no <entry-point> provided for --source-entrypoint"); break; } else if (lowerword == "keep-uncalled" || // synonyms lowerword == "ku") { Options |= EOptionKeepUncalled; } else { usage(); } } break; case 'H': Options |= EOptionHumanReadableSpv; if ((Options & EOptionSpv) == 0) { // default to Vulkan Options |= EOptionSpv; Options |= EOptionVulkanRules; Options |= EOptionLinkProgram; } break; case 'V': Options |= EOptionSpv; Options |= EOptionVulkanRules; Options |= EOptionLinkProgram; break; case 'S': shaderStageName = argv[1]; if (argc > 0) { argc--; argv++; } else Error("no <stage> specified for -S"); break; case 'G': Options |= EOptionSpv; Options |= EOptionLinkProgram; // undo a -H default to Vulkan Options &= ~EOptionVulkanRules; break; case 'E': Options |= EOptionOutputPreprocessed; break; case 'c': Options |= EOptionDumpConfig; break; case 'C': Options |= EOptionCascadingErrors; break; case 'd': Options |= EOptionDefaultDesktop; break; case 'D': Options |= EOptionReadHlsl; break; case 'e': // HLSL todo: entry point handle needs much more sophistication. // This is okay for one compilation unit with one entry point. entryPointName = argv[1]; if (argc > 0) { argc--; argv++; } else Error("no <entry-point> provided for -e"); break; case 'h': usage(); break; case 'i': Options |= EOptionIntermediate; break; case 'l': Options |= EOptionLinkProgram; break; case 'm': Options |= EOptionMemoryLeakMode; break; case 'o': binaryFileName = argv[1]; if (argc > 0) { argc--; argv++; } else Error("no <file> provided for -o"); break; case 'q': Options |= EOptionDumpReflection; break; case 'r': Options |= EOptionRelaxedErrors; break; case 's': Options |= EOptionSuppressInfolog; break; case 't': #ifdef _WIN32 Options |= EOptionMultiThreaded; #endif break; case 'v': Options |= EOptionDumpVersions; break; case 'w': Options |= EOptionSuppressWarnings; break; case 'x': Options |= EOptionOutputHexadecimal; break; default: usage(); break; } } else { std::string name(argv[0]); if (! SetConfigFile(name)) { Work[argc] = new glslang::TWorkItem(name); Worklist.add(Work[argc]); } } } // Make sure that -E is not specified alongside linking (which includes SPV generation) if ((Options & EOptionOutputPreprocessed) && (Options & EOptionLinkProgram)) Error("can't use -E when linking is selected"); // -o or -x makes no sense if there is no target binary if (binaryFileName && (Options & EOptionSpv) == 0) Error("no binary generation requested (e.g., -V)"); if ((Options & EOptionFlattenUniformArrays) != 0 && (Options & EOptionReadHlsl) == 0) Error("uniform array flattening only valid when compiling HLSL source."); }