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
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) {
#endif
}
- FreeFileData(data);
+ delete [] lengths;
+ FreeFileData(shaderStrings);
return ret ? true : false;
}
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);
ptr_len+=(len);
if(count<len){
if(count==0){
- OutputMultipleStrings=(i+1);
+ NumShaderStrings=(i+1);
break;
}
len = count;
void FreeFileData(char **data)
{
- for(int i=0;i<OutputMultipleStrings;i++)
+ for(int i=0;i<NumShaderStrings;i++)
free(data[i]);
}
<ClCompile Include="glslang\MachineIndependent\glslang_tab.cpp" />\r
<ClCompile Include="glslang\MachineIndependent\InfoSink.cpp" />\r
<ClCompile Include="glslang\MachineIndependent\Initialize.cpp" />\r
+ <ClCompile Include="glslang\MachineIndependent\Scan.cpp" />\r
<ClCompile Include="glslang\MachineIndependent\Versions.cpp" />\r
<ClCompile Include="OGLCompilersDLL\InitializeDll.cpp" />\r
<ClCompile Include="glslang\MachineIndependent\IntermTraverse.cpp" />\r
<ClInclude Include="glslang\Include\Common.h" />\r
<ClInclude Include="glslang\Include\ConstantUnion.h" />\r
<ClInclude Include="glslang\Include\InfoSink.h" />\r
+ <ClInclude Include="glslang\MachineIndependent\Scan.h" />\r
<ClInclude Include="glslang\MachineIndependent\Versions.h" />\r
<ClInclude Include="OGLCompilersDLL\InitializeDll.h" />\r
<ClInclude Include="glslang\Include\InitializeGlobals.h" />\r
<ClCompile Include="glslang\MachineIndependent\Constant.cpp">\r
<Filter>Machine Independent</Filter>\r
</ClCompile>\r
+ <ClCompile Include="glslang\MachineIndependent\Scan.cpp">\r
+ <Filter>Machine Independent</Filter>\r
+ </ClCompile>\r
</ItemGroup>\r
<ItemGroup>\r
<ClInclude Include="glslang\MachineIndependent\Initialize.h">\r
<ClInclude Include="glslang\MachineIndependent\Versions.h">\r
<Filter>Machine Independent</Filter>\r
</ClInclude>\r
+ <ClInclude Include="glslang\MachineIndependent\Scan.h">\r
+ <Filter>Machine Independent</Filter>\r
+ </ClInclude>\r
</ItemGroup>\r
<ItemGroup>\r
<CustomBuild Include="glslang\MachineIndependent\glslang.y">\r
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
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
--- /dev/null
+//
+//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 <string.h>
+
+#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
--- /dev/null
+//
+//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
#include <string.h>
#include "SymbolTable.h"
#include "ParseHelper.h"
+#include "Scan.h"
#include "../Include/ShHandle.h"
#include "InitializeDll.h"
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;
return true;
}
-
}; // end anonymous namespace for local functions
int ShInitialize()
const ShHandle handle,
const char* const shaderStrings[],
const int numStrings,
+ const int* inputLengths,
const EShOptimizationLevel optLevel,
const TBuiltInResource* resources,
int debugOptions,
EShMessages messages // warnings/errors
)
{
- if (!InitThread())
+ if (! InitThread())
return 0;
if (handle == 0)
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;
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;
if (parseContext.insertBuiltInArrayAtGlobalLevel())
success = false;
- int ret = PaParseStrings(const_cast<char**>(shaderStrings), 0, numStrings, parseContext, parseContext.getPreamble());
+ int ret = PaParseStrings(const_cast<char**>(shaderStrings), lengths, numStrings, parseContext, parseContext.getPreamble());
if (ret)
success = false;
intermediate.addSymbolLinkageNodes(parseContext.treeRoot, parseContext.linkage, parseContext.language, symbolTable);
// Throw away all the temporary memory used by the compilation process.
//
GlobalPoolAllocator.pop();
+ delete [] lengths;
return success ? 1 : 0;
}
const ShHandle,
const char* const shaderStrings[],
const int numStrings,
+ const int* lengths,
const EShOptimizationLevel,
const TBuiltInResource *resources,
int debugOptions,