diff options
Diffstat (limited to '3rdparty/bgfx/3rdparty/glslang/StandAlone/StandAlone.cpp')
-rw-r--r-- | 3rdparty/bgfx/3rdparty/glslang/StandAlone/StandAlone.cpp | 1106 |
1 files changed, 1106 insertions, 0 deletions
diff --git a/3rdparty/bgfx/3rdparty/glslang/StandAlone/StandAlone.cpp b/3rdparty/bgfx/3rdparty/glslang/StandAlone/StandAlone.cpp new file mode 100644 index 00000000000..d40673c1552 --- /dev/null +++ b/3rdparty/bgfx/3rdparty/glslang/StandAlone/StandAlone.cpp @@ -0,0 +1,1106 @@ +// +//Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +//Copyright (C) 2013-2016 LunarG, Inc. +// +//All rights reserved. +// +//Redistribution and use in source and binary forms, with or without +//modification, are permitted provided that the following conditions +//are met: +// +// Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// Neither the name of 3Dlabs Inc. Ltd. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +//POSSIBILITY OF SUCH DAMAGE. +// + +// this only applies to the standalone wrapper, not the front end in general +#define _CRT_SECURE_NO_WARNINGS + +#include "ResourceLimits.h" +#include "Worklist.h" +#include "./../glslang/Include/ShHandle.h" +#include "./../glslang/Include/revision.h" +#include "./../glslang/Public/ShaderLang.h" +#include "../SPIRV/GlslangToSpv.h" +#include "../SPIRV/GLSL.std.450.h" +#include "../SPIRV/doc.h" +#include "../SPIRV/disassemble.h" +#include <cstring> +#include <cstdlib> +#include <cctype> +#include <cmath> +#include <array> + +#include "../glslang/OSDependent/osinclude.h" + +extern "C" { + SH_IMPORT_EXPORT void ShOutputHtml(); +} + +// Command-line options +enum TOptions { + EOptionNone = 0, + EOptionIntermediate = (1 << 0), + EOptionSuppressInfolog = (1 << 1), + EOptionMemoryLeakMode = (1 << 2), + EOptionRelaxedErrors = (1 << 3), + EOptionGiveWarnings = (1 << 4), + EOptionLinkProgram = (1 << 5), + EOptionMultiThreaded = (1 << 6), + EOptionDumpConfig = (1 << 7), + EOptionDumpReflection = (1 << 8), + EOptionSuppressWarnings = (1 << 9), + EOptionDumpVersions = (1 << 10), + EOptionSpv = (1 << 11), + EOptionHumanReadableSpv = (1 << 12), + EOptionVulkanRules = (1 << 13), + EOptionDefaultDesktop = (1 << 14), + EOptionOutputPreprocessed = (1 << 15), + EOptionOutputHexadecimal = (1 << 16), + EOptionReadHlsl = (1 << 17), + EOptionCascadingErrors = (1 << 18), + EOptionAutoMapBindings = (1 << 19), + EOptionFlattenUniformArrays = (1 << 20), + EOptionNoStorageFormat = (1 << 21), + EOptionKeepUncalled = (1 << 22), +}; + +// +// Return codes from main/exit(). +// +enum TFailCode { + ESuccess = 0, + EFailUsage, + EFailCompile, + EFailLink, + EFailCompilerCreate, + EFailThreadCreate, + EFailLinkerCreate +}; + +// +// Forward declarations. +// +EShLanguage FindLanguage(const std::string& name, bool parseSuffix=true); +void CompileFile(const char* fileName, ShHandle); +void usage(); +void FreeFileData(char** data); +char** ReadFileData(const char* fileName); +void InfoLogMsg(const char* msg, const char* name, const int num); + +// Globally track if any compile or link failure. +bool CompileFailed = false; +bool LinkFailed = false; + +// Use to test breaking up a single shader file into multiple strings. +// Set in ReadFileData(). +int NumShaderStrings; + +TBuiltInResource Resources; +std::string ConfigFile; + +// +// Parse either a .conf file provided by the user or the default from glslang::DefaultTBuiltInResource +// +void ProcessConfigFile() +{ + char** configStrings = 0; + char* config = 0; + if (ConfigFile.size() > 0) { + configStrings = ReadFileData(ConfigFile.c_str()); + if (configStrings) + config = *configStrings; + else { + printf("Error opening configuration file; will instead use the default configuration\n"); + usage(); + } + } + + if (config == 0) { + Resources = glslang::DefaultTBuiltInResource; + return; + } + + glslang::DecodeResourceLimits(&Resources, config); + + if (configStrings) + FreeFileData(configStrings); + else + delete[] config; +} + +// thread-safe list of shaders to asynchronously grab and compile +glslang::TWorklist Worklist; + +// array of unique places to leave the shader names and infologs for the asynchronous compiles +glslang::TWorkItem** Work = 0; +int NumWorkItems = 0; + +int Options = 0; +const char* ExecutableName = nullptr; +const char* binaryFileName = nullptr; +const char* entryPointName = nullptr; +const char* sourceEntryPointName = nullptr; +const char* shaderStageName = nullptr; + +std::array<unsigned int, EShLangCount> baseSamplerBinding; +std::array<unsigned int, EShLangCount> baseTextureBinding; +std::array<unsigned int, EShLangCount> baseImageBinding; +std::array<unsigned int, EShLangCount> baseUboBinding; + +// +// Create the default name for saving a binary if -o is not provided. +// +const char* GetBinaryName(EShLanguage stage) +{ + const char* name; + if (binaryFileName == nullptr) { + switch (stage) { + case EShLangVertex: name = "vert.spv"; break; + case EShLangTessControl: name = "tesc.spv"; break; + case EShLangTessEvaluation: name = "tese.spv"; break; + case EShLangGeometry: name = "geom.spv"; break; + case EShLangFragment: name = "frag.spv"; break; + case EShLangCompute: name = "comp.spv"; break; + default: name = "unknown"; break; + } + } else + name = binaryFileName; + + return name; +} + +// +// *.conf => this is a config file that can set limits/resources +// +bool SetConfigFile(const std::string& name) +{ + if (name.size() < 5) + return false; + + if (name.compare(name.size() - 5, 5, ".conf") == 0) { + ConfigFile = name; + return true; + } + + return false; +} + +// +// Give error and exit with failure code. +// +void Error(const char* message) +{ + printf("%s: Error %s (use -h for usage)\n", ExecutableName, message); + exit(EFailUsage); +} + +// +// Process an optional binding base of the form: +// --argname [stage] base +// Where stage is one of the forms accepted by FindLanguage, and base is an integer +// +void ProcessBindingBase(int& argc, char**& argv, std::array<unsigned int, EShLangCount>& base) +{ + if (argc < 2) + usage(); + + if (!isdigit(argv[1][0])) { + if (argc < 3) // this form needs one more argument + usage(); + + // Parse form: --argname stage base + const EShLanguage lang = FindLanguage(argv[1], false); + base[lang] = atoi(argv[2]); + argc-= 2; + argv+= 2; + } else { + // Parse form: --argname base + for (int lang=0; lang<EShLangCount; ++lang) + base[lang] = atoi(argv[1]); + + argc--; + argv++; + } +} + +// +// 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."); +} + +// +// Translate the meaningful subset of command-line options to parser-behavior options. +// +void SetMessageOptions(EShMessages& messages) +{ + if (Options & EOptionRelaxedErrors) + messages = (EShMessages)(messages | EShMsgRelaxedErrors); + if (Options & EOptionIntermediate) + messages = (EShMessages)(messages | EShMsgAST); + if (Options & EOptionSuppressWarnings) + messages = (EShMessages)(messages | EShMsgSuppressWarnings); + if (Options & EOptionSpv) + messages = (EShMessages)(messages | EShMsgSpvRules); + if (Options & EOptionVulkanRules) + messages = (EShMessages)(messages | EShMsgVulkanRules); + if (Options & EOptionOutputPreprocessed) + messages = (EShMessages)(messages | EShMsgOnlyPreprocessor); + if (Options & EOptionReadHlsl) + messages = (EShMessages)(messages | EShMsgReadHlsl); + if (Options & EOptionCascadingErrors) + messages = (EShMessages)(messages | EShMsgCascadingErrors); + if (Options & EOptionKeepUncalled) + messages = (EShMessages)(messages | EShMsgKeepUncalled); +} + +// +// 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; +} + +// Outputs the given string, but only if it is non-null and non-empty. +// This prevents erroneous newlines from appearing. +void PutsIfNonEmpty(const char* str) +{ + if (str && str[0]) { + puts(str); + } +} + +// Outputs the given string to stderr, but only if it is non-null and non-empty. +// This prevents erroneous newlines from appearing. +void StderrIfNonEmpty(const char* str) +{ + if (str && str[0]) { + fprintf(stderr, "%s\n", str); + } +} + +// Simple bundling of what makes a compilation unit for ease in passing around, +// and separation of handling file IO versus API (programmatic) compilation. +struct ShaderCompUnit { + EShLanguage stage; + std::string fileName; + char** text; // memory owned/managed externally + const char* fileNameList[1]; + + // Need to have a special constructors to adjust the fileNameList, since back end needs a list of ptrs + ShaderCompUnit(EShLanguage istage, std::string &ifileName, char** itext) + { + stage = istage; + fileName = ifileName; + text = itext; + fileNameList[0] = fileName.c_str(); + } + + ShaderCompUnit(const ShaderCompUnit &rhs) + { + stage = rhs.stage; + fileName = rhs.fileName; + text = rhs.text; + fileNameList[0] = fileName.c_str(); + } + +}; + +// +// For linking mode: Will independently parse each compilation unit, but then put them +// in the same program and link them together, making at most one linked module per +// pipeline stage. +// +// Uses the new C++ interface instead of the old handle-based interface. +// + +void CompileAndLinkShaderUnits(std::vector<ShaderCompUnit> compUnits) +{ + // 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; + for (auto it = compUnits.cbegin(); it != compUnits.cend(); ++it) { + const auto &compUnit = *it; + glslang::TShader* shader = new glslang::TShader(compUnit.stage); + shader->setStringsWithLengthsAndNames(compUnit.text, NULL, compUnit.fileNameList, 1); + if (entryPointName) // HLSL todo: this needs to be tracked per compUnits + shader->setEntryPoint(entryPointName); + if (sourceEntryPointName) + shader->setSourceEntryPoint(sourceEntryPointName); + + shader->setShiftSamplerBinding(baseSamplerBinding[compUnit.stage]); + shader->setShiftTextureBinding(baseTextureBinding[compUnit.stage]); + shader->setShiftImageBinding(baseImageBinding[compUnit.stage]); + shader->setShiftUboBinding(baseUboBinding[compUnit.stage]); + shader->setFlattenUniformArrays((Options & EOptionFlattenUniformArrays) != 0); + shader->setNoStorageFormat((Options & EOptionNoStorageFormat) != 0); + + if (Options & EOptionAutoMapBindings) + shader->setAutoMapBindings(true); + + shaders.push_back(shader); + + const int defaultVersion = Options & EOptionDefaultDesktop? 110: 100; + + if (Options & EOptionOutputPreprocessed) { + std::string str; + glslang::TShader::ForbidInclude includer; + if (shader->preprocess(&Resources, defaultVersion, ENoProfile, false, false, + messages, &str, includer)) { + PutsIfNonEmpty(str.c_str()); + } else { + CompileFailed = true; + } + StderrIfNonEmpty(shader->getInfoLog()); + StderrIfNonEmpty(shader->getInfoDebugLog()); + continue; + } + if (! shader->parse(&Resources, defaultVersion, false, messages)) + CompileFailed = true; + + program.addShader(shader); + + if (! (Options & EOptionSuppressInfolog) && + ! (Options & EOptionMemoryLeakMode)) { + PutsIfNonEmpty(compUnit.fileName.c_str()); + PutsIfNonEmpty(shader->getInfoLog()); + PutsIfNonEmpty(shader->getInfoDebugLog()); + } + } + + // + // Program-level processing... + // + + // Link + if (! (Options & EOptionOutputPreprocessed) && ! program.link(messages)) + LinkFailed = true; + + // Map IO + if (Options & EOptionSpv) { + if (!program.mapIO()) + LinkFailed = true; + } + + // Report + if (! (Options & EOptionSuppressInfolog) && + ! (Options & EOptionMemoryLeakMode)) { + PutsIfNonEmpty(program.getInfoLog()); + PutsIfNonEmpty(program.getInfoDebugLog()); + } + + // Reflect + if (Options & EOptionDumpReflection) { + program.buildReflection(); + program.dumpReflection(); + } + + // Dump SPIR-V + 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; + std::string warningsErrors; + spv::SpvBuildLogger logger; + glslang::GlslangToSpv(*program.getIntermediate((EShLanguage)stage), spirv, &logger); + + // Dump the spv to a file or stdout, etc., but only if not doing + // memory/perf testing, as it's not internal to programmatic use. + if (! (Options & EOptionMemoryLeakMode)) { + printf("%s", logger.getAllMessages().c_str()); + if (Options & EOptionOutputHexadecimal) { + glslang::OutputSpvHex(spirv, GetBinaryName((EShLanguage)stage)); + } else { + glslang::OutputSpvBin(spirv, GetBinaryName((EShLanguage)stage)); + } + if (Options & EOptionHumanReadableSpv) { + 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 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", glslang::GetDefaultTBuiltInResourceString().c_str()); + 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); + printf("Khronos Tool ID %d\n", glslang::GetKhronosToolId()); + printf("GL_KHR_vulkan_glsl version %d\n", 100); + printf("ARB_GL_gl_spirv version %d\n", 100); + 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(); + CompileAndLinkShaderFiles(); + glslang::FinalizeProcess(); + for (int w = 0; w < NumWorkItems; ++w) { + if (Work[w]) { + delete Work[w]; + } + } + } 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 || Work[w]->results.size() > 0) + PutsIfNonEmpty(Work[w]->name.c_str()); + PutsIfNonEmpty(Work[w]->results.c_str()); + delete Work[w]; + } + } + + ShFinalize(); + } + + delete[] Work; + + if (CompileFailed) + return EFailCompile; + if (LinkFailed) + return EFailLink; + + return 0; +} + +// +// Deduce the language from the filename. Files must end in one of the +// following extensions: +// +// .vert = vertex +// .tesc = tessellation control +// .tese = tessellation evaluation +// .geom = geometry +// .frag = fragment +// .comp = compute +// +EShLanguage FindLanguage(const std::string& name, bool parseSuffix) +{ + size_t ext = 0; + + // Search for a suffix on a filename: e.g, "myfile.frag". If given + // the suffix directly, we skip looking the '.' + if (parseSuffix) { + ext = name.rfind('.'); + if (ext == std::string::npos) { + usage(); + return EShLangVertex; + } + ++ext; + } + + std::string suffix = name.substr(ext, std::string::npos); + if (shaderStageName) + suffix = shaderStageName; + + if (suffix == "vert") + return EShLangVertex; + else if (suffix == "tesc") + return EShLangTessControl; + else if (suffix == "tese") + return EShLangTessEvaluation; + else if (suffix == "geom") + return EShLangGeometry; + else if (suffix == "frag") + return EShLangFragment; + else if (suffix == "comp") + return EShLangCompute; + + usage(); + return EShLangVertex; +} + +// +// Read a file's data into a string, and compile it using the old interface ShCompile, +// for non-linkable results. +// +void CompileFile(const char* fileName, ShHandle compiler) +{ + int ret = 0; + char** shaderStrings = ReadFileData(fileName); + if (! shaderStrings) { + usage(); + } + + int* lengths = new int[NumShaderStrings]; + + // move to length-based strings, rather than null-terminated strings + for (int s = 0; s < NumShaderStrings; ++s) + lengths[s] = (int)strlen(shaderStrings[s]); + + if (! shaderStrings) { + CompileFailed = true; + return; + } + + EShMessages messages = EShMsgDefault; + SetMessageOptions(messages); + + for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { + for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) { + //ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); + ret = ShCompile(compiler, shaderStrings, NumShaderStrings, nullptr, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); + //const char* multi[12] = { "# ve", "rsion", " 300 e", "s", "\n#err", + // "or should be l", "ine 1", "string 5\n", "float glo", "bal", + // ";\n#error should be line 2\n void main() {", "global = 2.3;}" }; + //const char* multi[7] = { "/", "/", "\\", "\n", "\n", "#", "version 300 es" }; + //ret = ShCompile(compiler, multi, 7, nullptr, EShOptNone, &Resources, Options, (Options & EOptionDefaultDesktop) ? 110 : 100, false, messages); + } + + if (Options & EOptionMemoryLeakMode) + glslang::OS_DumpMemoryCounters(); + } + + delete [] lengths; + FreeFileData(shaderStrings); + + if (ret == 0) + CompileFailed = true; +} + +// +// print usage to stdout +// +void usage() +{ + printf("Usage: glslangValidator [option]... [file]...\n" + "\n" + "Where: each 'file' ends in .<stage>, where <stage> is one of\n" + " .conf to provide an optional config file that replaces the default configuration\n" + " (see -c option below for generating a template)\n" + " .vert for a vertex shader\n" + " .tesc for a tessellation control shader\n" + " .tese for a tessellation evaluation shader\n" + " .geom for a geometry shader\n" + " .frag for a fragment shader\n" + " .comp for a compute shader\n" + "\n" + "Compilation warnings and errors will be printed to stdout.\n" + "\n" + "To get other information, use one of the following options:\n" + "Each option must be specified separately.\n" + " -V create SPIR-V binary, under Vulkan semantics; turns on -l;\n" + " default file name is <stage>.spv (-o overrides this)\n" + " -G create SPIR-V binary, under OpenGL semantics; turns on -l;\n" + " default file name is <stage>.spv (-o overrides this)\n" + " -H print human readable form of SPIR-V; turns on -V\n" + " -E print pre-processed GLSL; cannot be used with -l;\n" + " errors will appear on stderr.\n" + " -S <stage> uses explicit stage specified, rather then the file extension.\n" + " valid choices are vert, tesc, tese, geom, frag, or comp\n" + " -c configuration dump;\n" + " creates the default configuration file (redirect to a .conf file)\n" + " -C cascading errors; risks crashes from accumulation of error recoveries\n" + " -d default to desktop (#version 110) when there is no shader #version\n" + " (default is ES version 100)\n" + " -D input is HLSL\n" + " -e specify entry-point name\n" + " -h print this usage message\n" + " -i intermediate tree (glslang AST) is printed out\n" + " -l link all input files together to form a single module\n" + " -m memory leak mode\n" + " -o <file> save binary to <file>, requires a binary option (e.g., -V)\n" + " -q dump reflection query database\n" + " -r relaxed semantic error-checking mode\n" + " -s silent mode\n" + " -t multi-threaded mode\n" + " -v print version strings\n" + " -w suppress warnings (except as required by #extension : warn)\n" + " -x save 32-bit hexadecimal numbers as text, requires a binary option (e.g., -V)\n" + "\n" + " --shift-sampler-binding [stage] num set base binding number for samplers\n" + " --ssb [stage] num synonym for --shift-sampler-binding\n" + "\n" + " --shift-texture-binding [stage] num set base binding number for textures\n" + " --stb [stage] num synonym for --shift-texture-binding\n" + "\n" + " --shift-image-binding [stage] num set base binding number for images (uav)\n" + " --sib [stage] num synonym for --shift-image-binding\n" + "\n" + " --shift-UBO-binding [stage] num set base binding number for UBOs\n" + " --sub [stage] num synonym for --shift-UBO-binding\n" + "\n" + " --auto-map-bindings automatically bind uniform variables without\n" + " explicit bindings.\n" + " --amb synonym for --auto-map-bindings\n" + "\n" + " --flatten-uniform-arrays flatten uniform texture & sampler arrays to scalars\n" + " --fua synonym for --flatten-uniform-arrays\n" + "\n" + " --no-storage-format use Unknown image format\n" + " --nsf synonym for --no-storage-format\n" + "\n" + " --source-entrypoint name the given shader source function is renamed to be the entry point given in -e\n" + " --sep synonym for --source-entrypoint\n" + "\n" + " --keep-uncalled don't eliminate uncalled functions when linking\n" + " --ku synonym for --keep-uncalled\n" + ); + + exit(EFailUsage); +} + +#if !defined _MSC_VER && !defined MINGW_HAS_SECURE_API + +#include <errno.h> + +int fopen_s( + FILE** pFile, + const char* filename, + const char* mode +) +{ + if (!pFile || !filename || !mode) { + return EINVAL; + } + + FILE* f = fopen(filename, mode); + if (! f) { + if (errno != 0) { + return errno; + } else { + return ENOENT; + } + } + *pFile = f; + + return 0; +} + +#endif + +// +// Malloc a string of sufficient size and read a string into it. +// +char** ReadFileData(const char* fileName) +{ + FILE *in = nullptr; + int errorCode = fopen_s(&in, fileName, "r"); + + int count = 0; + const int maxSourceStrings = 5; // for testing splitting shader/tokens across multiple strings + char** return_data = (char**)malloc(sizeof(char *) * (maxSourceStrings+1)); // freed in FreeFileData() + + if (errorCode || in == nullptr) + Error("unable to open input file"); + + while (fgetc(in) != EOF) + count++; + + fseek(in, 0, SEEK_SET); + + char *fdata = (char*)malloc(count+2); // freed before return of this function + if (! fdata) + Error("can't allocate memory"); + + if ((int)fread(fdata, 1, count, in) != count) { + free(fdata); + Error("can't read input file"); + } + + fdata[count] = '\0'; + fclose(in); + + if (count == 0) { + // recover from empty file + return_data[0] = (char*)malloc(count+2); // freed in FreeFileData() + return_data[0][0]='\0'; + NumShaderStrings = 0; + free(fdata); + + return return_data; + } else + NumShaderStrings = 1; // Set to larger than 1 for testing multiple strings + + // compute how to split up the file into multiple strings, for testing multiple strings + int len = (int)(ceil)((float)count/(float)NumShaderStrings); + int ptr_len = 0; + int i = 0; + while (count > 0) { + return_data[i] = (char*)malloc(len + 2); // freed in FreeFileData() + memcpy(return_data[i], fdata + ptr_len, len); + return_data[i][len] = '\0'; + count -= len; + ptr_len += len; + if (count < len) { + if (count == 0) { + NumShaderStrings = i + 1; + break; + } + len = count; + } + ++i; + } + + free(fdata); + + return return_data; +} + +void FreeFileData(char** data) +{ + for(int i = 0; i < NumShaderStrings; i++) + free(data[i]); + + free(data); +} + +void InfoLogMsg(const char* msg, const char* name, const int num) +{ + if (num >= 0 ) + printf("#### %s %s %d INFO LOG ####\n", msg, name, num); + else + printf("#### %s %s INFO LOG ####\n", msg, name); +} |