diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index cf58ed69..e0915af1 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -79,8 +79,9 @@ void usage(); void FreeFileData(char **data); char** ReadFileData(const char *fileName); void InfoLogMsg(const char* msg, const char* name, const int num); -//Added to accomodate the multiple strings. -int OutputMultipleStrings = 1; + +// Use to test breaking a single shader file into multiple strings. +int NumShaderStrings = 1; // // Set up the per compile resources @@ -255,21 +256,31 @@ static EShLanguage FindLanguage(char *name) bool CompileFile(const char *fileName, ShHandle compiler, int debugOptions, const TBuiltInResource *resources) { int ret; - char **data = ReadFileData(fileName); + char** shaderStrings = ReadFileData(fileName); + int* lengths = new int[NumShaderStrings]; + + // move to length-based strings, rather than null-terminated strings + for (int s = 0; s < NumShaderStrings; ++s) + lengths[s] = strlen(shaderStrings[s]); #ifdef _WIN32 PROCESS_MEMORY_COUNTERS counters; // just for memory leak testing #endif - if (!data) + if (! shaderStrings) return false; EShMessages messages = EShMsgDefault; if (debugOptions & EDebugOpRelaxedErrors) messages = (EShMessages)(messages | EShMsgRelaxedErrors); for (int i = 0; i < ((debugOptions & EDebugOpMemoryLeakMode) ? 100 : 1); ++i) { - for (int j = 0; j < ((debugOptions & EDebugOpMemoryLeakMode) ? 100 : 1); ++j) - ret = ShCompile(compiler, data, OutputMultipleStrings, EShOptNone, resources, debugOptions, 100, false, messages); + for (int j = 0; j < ((debugOptions & EDebugOpMemoryLeakMode) ? 100 : 1); ++j) { + //ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, resources, debugOptions, 100, false, messages); + ret = ShCompile(compiler, shaderStrings, NumShaderStrings, 0, EShOptNone, resources, debugOptions, 100, false, messages); + //const char* multi[4] = { "# ve", "rsion", " 300 e", "s" }; + //const char* multi[7] = { "/", "/", "\\", "\n", "\n", "#", "version 300 es" }; + //ret = ShCompile(compiler, multi, 4, 0, EShOptNone, resources, debugOptions, 100, false, messages); + } #ifdef _WIN32 if (debugOptions & EDebugOpMemoryLeakMode) { @@ -279,7 +290,8 @@ bool CompileFile(const char *fileName, ShHandle compiler, int debugOptions, cons #endif } - FreeFileData(data); + delete [] lengths; + FreeFileData(shaderStrings); return ret ? true : false; } @@ -366,11 +378,11 @@ char** ReadFileData(const char *fileName) if(count==0){ return_data[0]=(char*)malloc(count+2); return_data[0][0]='\0'; - OutputMultipleStrings=0; + NumShaderStrings=0; return return_data; } - int len = (int)(ceil)((float)count/(float)OutputMultipleStrings); + int len = (int)(ceil)((float)count/(float)NumShaderStrings); int ptr_len=0,i=0; while(count>0){ return_data[i]=(char*)malloc(len+2); @@ -380,7 +392,7 @@ char** ReadFileData(const char *fileName) ptr_len+=(len); if(count + @@ -204,6 +205,7 @@ xcopy /y $(IntDir)$(TargetName)$(TargetExt) Test + diff --git a/glslang.vcxproj.filters b/glslang.vcxproj.filters index 4083edcd..3e5cfbd6 100644 --- a/glslang.vcxproj.filters +++ b/glslang.vcxproj.filters @@ -109,6 +109,9 @@ Machine Independent + + Machine Independent + @@ -216,6 +219,9 @@ Machine Independent + + Machine Independent + diff --git a/glslang/MachineIndependent/Makefile b/glslang/MachineIndependent/Makefile index 4a84f67b..508c29fe 100644 --- a/glslang/MachineIndependent/Makefile +++ b/glslang/MachineIndependent/Makefile @@ -11,12 +11,12 @@ LIBCODEGEN=./../GenericCodeGen/libCodeGen.a OBJECTS= Initialize.o IntermTraverse.o \ Intermediate.o ParseHelper.o PoolAlloc.o QualifierAlive.o \ RemoveTree.o ShaderLang.o intermOut.o parseConst.o SymbolTable.o \ - InfoSink.o Versions.o Constant.o + InfoSink.o Versions.o Constant.o Scan.o SRCS= gen_glslang.cpp gen_glslang_tab.cpp Initialize.cpp IntermTraverse.cpp \ Intermediate.cpp ParseHelper.cpp PoolAlloc.cp QualifierAlive.cpp \ RemoveTree.cpp ShaderLang.cpp SymbolTable.cpp intermOut.cpp \ - parseConst.cpp InfoSink.cpp Versions.cpp Constant.cpp + parseConst.cpp InfoSink.cpp Versions.cpp Constant.cpp Scan.cpp CPPFLAGS=$(DEFINE) $(INCLUDE) -fPIC SHAREDOBJECT=./lib/libglslang.so @@ -161,3 +161,4 @@ parseConst.o: ../Public/ShaderLang.h InfoSink.o: ../Include/InfoSink.h Versions.o: ParseHelper.h Versions.h ../Include/ShHandle.h SymbolTable.h localintermediate.h Constant.o: localintermediate.h ../Include/intermediate.h ../Public/ShaderLang.h SymbolTable.h Versions.h +Scan.o: Scan.h Versions.h diff --git a/glslang/MachineIndependent/Scan.cpp b/glslang/MachineIndependent/Scan.cpp new file mode 100644 index 00000000..d7f1119e --- /dev/null +++ b/glslang/MachineIndependent/Scan.cpp @@ -0,0 +1,216 @@ +// +//Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +//Copyright (C) 2013 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. +// + +#include + +#include "Scan.h" + +namespace glslang { + +// read past any white space +void ConsumeWhiteSpace(TInputScanner& input, bool& foundNonSpaceTab) +{ + char c = input.peek(); // don't accidentally consume anything other than whitespace + while (c == ' ' || c == '\t' || c == '\r' || c == '\n') { + if (c == '\r' || c == '\n') + foundNonSpaceTab = true; + input.get(); + c = input.peek(); + } +} + +// return true if a comment was actually consumed +bool ConsumeComment(TInputScanner& input) +{ + if (input.peek() != '/') + return false; + + input.get(); // consume the '/' + char c = input.peek(); + if (c == '/') { + + // a '//' style comment + input.get(); // consume the second '/' + c = input.get(); + do { + while (c > 0 && c != '\\' && c != '\r' && c != '\n') + c = input.get(); + + if (c <= 0 || c == '\r' || c == '\n') { + while (c == '\r' || c == '\n') + c = input.get(); + + // we reached the end of the comment + break; + } else { + // it's a '\', so we need to keep going, after skipping what's escaped + + // read the skipped character + c = input.get(); + + // if it's a two-character newline, skip both characters + if (c == '\r' && input.peek() == '\n') + input.get(); + c = input.get(); + } + } while (true); + + // put back the last non-comment character + if (c > 0) + input.unget(); + + return true; + } else if (c == '*') { + + // a '/*' style comment + input.get(); // consume the '*' + c = input.get(); + do { + while (c > 0 && c != '*') + c = input.get(); + if (c == '*') { + c = input.get(); + if (c == '/') + break; // end of comment + // not end of comment + } else // end of input + break; + } while (true); + + return true; + } else { + // it's not a comment, put the '/' back + input.unget(); + + return false; + } +} + +// skip whitespace, then skip a comment, rinse, repeat +void ConsumeWhitespaceComment(TInputScanner& input, bool& foundNonSpaceTab) +{ + do { + ConsumeWhiteSpace(input, foundNonSpaceTab); + + // if not starting a comment now, then done + char c = input.peek(); + if (c != '/' || c < 0) + return; + + // skip potential comment + foundNonSpaceTab = true; + if (! ConsumeComment(input)) + return; + + } while (true); +} + +// Returns true if there was non-white space (e.g., a comment, newline) before the #version; +// otherwise, returns true. +// +// N.B. does not attempt to leave input in any particular known state +bool ScanVersion(TInputScanner& input, int& version, EProfile& profile) +{ + // This function doesn't have to get all the semantics correct, + // just find the #version if there is a correct one present. + // The preprocessor will have the responsibility of getting all the semantics right. + + version = 0; // means not found + profile = ENoProfile; + + bool foundNonSpaceTab = false; + ConsumeWhitespaceComment(input, foundNonSpaceTab); + + // # + if (input.get() != '#') + return true; + + // whitespace + char c; + do { + c = input.get(); + } while (c == ' ' || c == '\t'); + + if ( c != 'v' || + input.get() != 'e' || + input.get() != 'r' || + input.get() != 's' || + input.get() != 'i' || + input.get() != 'o' || + input.get() != 'n') + return true; + + // whitespace + do { + c = input.get(); + } while (c == ' ' || c == '\t'); + + // version number + while (c >= '0' && c <= '9') { + version = 10 * version + (c - '0'); + c = input.get(); + } + if (version == 0) + return true; + + // whitespace + while (c == ' ' || c == '\t') + c = input.get(); + + // profile + const int maxProfileLength = 13; // not including any 0 + char profileString[maxProfileLength]; + int profileLength; + for (profileLength = 0; profileLength < maxProfileLength; ++profileLength) { + if (c < 0 || c == ' ' || c == '\t' || c == '\n' || c == '\r') + break; + profileString[profileLength] = c; + c = input.get(); + } + if (c > 0 && c != ' ' && c != '\t' && c != '\n' && c != '\r') + return true; + + if (profileLength == 2 && strncmp(profileString, "es", profileLength) == 0) + profile = EEsProfile; + else if (profileLength == 4 && strncmp(profileString, "core", profileLength) == 0) + profile = ECoreProfile; + else if (profileLength == 13 && strncmp(profileString, "compatibility", profileLength) == 0) + profile = ECompatibilityProfile; + + return foundNonSpaceTab; +} + +}; // end glslang namespace diff --git a/glslang/MachineIndependent/Scan.h b/glslang/MachineIndependent/Scan.h new file mode 100644 index 00000000..1ea57ee3 --- /dev/null +++ b/glslang/MachineIndependent/Scan.h @@ -0,0 +1,115 @@ +// +//Copyright (C) 2002-2005 3Dlabs Inc. Ltd. +//Copyright (C) 2013 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. +// + +#include "Versions.h" + +namespace glslang { + +// +// A character scanner that seamlessly, on read-only strings, reads across an +// array of strings without assuming null termination. +// +class TInputScanner { +public: + TInputScanner(int n, const char* const i[], int L[]) : numSources(n), sources(i), lengths(L), currentSource(0), currentChar(0) { } + + // return of -1 means end of strings, + // anything else is the next character + + // retrieve the next character and advance one character + char get() + { + if (currentSource >= numSources) + return -1; + + char ret = sources[currentSource][currentChar]; + advance(); + + return ret; + } + + // advance one character + void advance() + { + ++currentChar; + if (currentChar >= lengths[currentSource]) { + ++currentSource; + currentChar = 0; + while (currentSource < numSources && lengths[currentSource] == 0) + ++currentSource; + } + } + + // retrieve the next character, no advance + char peek() + { + if (currentSource >= numSources) + return -1; + + return sources[currentSource][currentChar]; + } + + // go back one character + void unget() + { + if (currentChar > 0) + --currentChar; + else { + do { + --currentSource; + } while (currentSource > 0 && lengths[currentSource] == 0); + currentChar = lengths[currentSource] - 1; + if (currentChar < 0) + currentChar = 0; + } + } + +protected: + int numSources; // number of strings in source + const char* const *sources; // array of strings + const int *lengths; // length of each string + int currentSource; + int currentChar; +}; + +// The location of these is still pending a grand design for going to a singular +// scanner for version finding, preprocessing, and tokenizing: +void ConsumeWhiteSpace(TInputScanner& input, bool& foundNonSpaceTab); +bool ConsumeComment(TInputScanner& input); +void ConsumeWhitespaceComment(TInputScanner& input, bool& foundNonSpaceTab); +bool ScanVersion(TInputScanner& input, int& version, EProfile& profile); + +}; // end glslang namespace diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index f2dbfd68..0c1bd9a3 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -43,6 +43,7 @@ #include #include "SymbolTable.h" #include "ParseHelper.h" +#include "Scan.h" #include "../Include/ShHandle.h" #include "InitializeDll.h" @@ -206,132 +207,6 @@ void SetupBuiltinSymbolTable(int version, EProfile profile) SetGlobalPoolAllocatorPtr(savedGPA); } -// returns true if something whas consumed -bool ConsumeWhitespaceComment(const char*& s) -{ - const char* startPoint = s; - - // first, skip white space - while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') { - ++s; - } - - // then, check for a comment - if (*s == '/') { - if (*(s+1) == '/') { - - // a '//' style comment - s += 2; - do { - while (*s && *s != '\\' && *s != '\r' && *s != '\n') - ++s; - - if (*s == '\r' || *s == '\n' || *s == 0) { - while (*s == '\r' || *s == '\n') - ++s; - - // we reached the end of the comment - break; - } else { - // it's a '\', so we need to keep going, after skipping what's escaped - ++s; - if (*s == '\r' && *(s+1) == '\n') - s += 2; - else { - // skip the escaped character - if (*s) - ++s; - } - } - } while (true); - - } else if (*(s+1) == '*') { - - // a '/*' style comment - s += 2; - do { - while (*s && *s != '*') - ++s; - if (*s == '*') { - ++s; - if (*s == '/') { - ++s; - break; - } // else not end of comment, keep going - } else // end of string - break; - } while (true); - } // else it's not a comment - } // else it's not a comment - - return startPoint != s; -} - -void ScanVersion(const char* const shaderStrings[], int numStrings, int& version, EProfile& profile) -{ - // This function doesn't have to get all the semantics correct, - // just find the #version if there is a correct one present. - // The CPP will have the responsibility of getting all the semantics right. - - version = 0; // means not found - profile = ENoProfile; - - const char* s = &shaderStrings[0][0]; - - // TODO: semantics: ES error check: #version must be on first line - - while (ConsumeWhitespaceComment(s)) - ; - - // # - if (*s != '#') - return; - ++s; - - // whitespace - while (*s == ' ' || *s == '\t') { - ++s; - } - - // version - if (strncmp(s, "version", 7) != 0) - return; - - // whitespace - s += 7; - while (*s == ' ' || *s == '\t') { - ++s; - } - - // version number - while (*s >= '0' && *s <= '9') { - version = 10 * version + (*s - '0'); - ++s; - } - if (version == 0) - return; - - // whitespace - while (*s == ' ' || *s == '\t') { - ++s; - } - - // profile - const char* end = s; - while (*end != ' ' && *end != '\t' && *end != '\n' && *end != '\r') { - if (*end == 0) - return; - ++end; - } - int profileLength = end - s; - if (profileLength == 2 && strncmp(s, "es", profileLength) == 0) - profile = EEsProfile; - else if (profileLength == 4 && strncmp(s, "core", profileLength) == 0) - profile = ECoreProfile; - else if (profileLength == 13 && strncmp(s, "compatibility", profileLength) == 0) - profile = ECompatibilityProfile; -} - bool DeduceProfile(TInfoSink& infoSink, int version, EProfile& profile) { const int FirstProfileVersion = 150; @@ -382,7 +257,6 @@ bool DeduceProfile(TInfoSink& infoSink, int version, EProfile& profile) return true; } - }; // end anonymous namespace for local functions int ShInitialize() @@ -479,6 +353,7 @@ int ShCompile( const ShHandle handle, const char* const shaderStrings[], const int numStrings, + const int* inputLengths, const EShOptimizationLevel optLevel, const TBuiltInResource* resources, int debugOptions, @@ -487,7 +362,7 @@ int ShCompile( EShMessages messages // warnings/errors ) { - if (!InitThread()) + if (! InitThread()) return 0; if (handle == 0) @@ -497,17 +372,28 @@ int ShCompile( if (compiler == 0) return 0; - GlobalPoolAllocator.push(); compiler->infoSink.info.erase(); compiler->infoSink.debug.erase(); if (numStrings == 0) return 1; + GlobalPoolAllocator.push(); + + // move to length-based strings, rather than null-terminated strings + int* lengths = new int[numStrings]; + for (int s = 0; s < numStrings; ++s) { + if (inputLengths == 0 || inputLengths[s] < 0) + lengths[s] = strlen(shaderStrings[s]); + else + lengths[s] = inputLengths[s]; + } + int version; EProfile profile; bool versionStatementMissing = false; - ScanVersion(shaderStrings, numStrings, version, profile); + glslang::TInputScanner input(numStrings, shaderStrings, lengths); + bool versionNotFirst = ScanVersion(input, version, profile); if (version == 0) { version = defaultVersion; versionStatementMissing = true; @@ -533,6 +419,8 @@ int ShCompile( parseContext.initializeExtensionBehavior(); if (versionStatementMissing) parseContext.warn(1, "statement missing: use #version on first line of shader", "#version", ""); + else if (profile == EEsProfile && version >= 300 && versionNotFirst) + parseContext.error(1, "statement must appear first in ESSL shader; before comments or newlines", "#version", ""); GlobalParseContext = &parseContext; @@ -553,7 +441,7 @@ int ShCompile( if (parseContext.insertBuiltInArrayAtGlobalLevel()) success = false; - int ret = PaParseStrings(const_cast(shaderStrings), 0, numStrings, parseContext, parseContext.getPreamble()); + int ret = PaParseStrings(const_cast(shaderStrings), lengths, numStrings, parseContext, parseContext.getPreamble()); if (ret) success = false; intermediate.addSymbolLinkageNodes(parseContext.treeRoot, parseContext.linkage, parseContext.language, symbolTable); @@ -597,6 +485,7 @@ int ShCompile( // Throw away all the temporary memory used by the compilation process. // GlobalPoolAllocator.pop(); + delete [] lengths; return success ? 1 : 0; } diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index df9dcdf9..d20b5d73 100644 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -164,6 +164,7 @@ SH_IMPORT_EXPORT int ShCompile( const ShHandle, const char* const shaderStrings[], const int numStrings, + const int* lengths, const EShOptimizationLevel, const TBuiltInResource *resources, int debugOptions,