Add link validation infrastructure for multiple compilation units per stage. Includes a new, straightforward, C++ interface to the front end.

git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@22927 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich 2013-09-04 21:19:27 +00:00
parent 2f1eb37d82
commit 69f4b517c2
24 changed files with 655 additions and 161 deletions

View File

@ -161,18 +161,16 @@ bool ProcessArguments(int argc, char* argv[])
return true; return true;
} }
// Thread entry point // Thread entry point, for non-linking asynchronous mode.
unsigned int unsigned int
#ifdef _WIN32 #ifdef _WIN32
__stdcall __stdcall
#endif #endif
CompileShaders(void*) CompileShaders(void*)
{ {
ShHandle compiler;
std::string shaderName; std::string shaderName;
while (Worklist.remove(shaderName)) { while (Worklist.remove(shaderName)) {
compiler = ShConstructCompiler(FindLanguage(shaderName), Options); ShHandle compiler = ShConstructCompiler(FindLanguage(shaderName), Options);
if (compiler == 0) if (compiler == 0)
return false; return false;
@ -189,6 +187,77 @@ CompileShaders(void*)
return 0; 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;
if (Options & EOptionRelaxedErrors)
messages = (EShMessages)(messages | EShMsgRelaxedErrors);
if (Options & EOptionIntermediate)
messages = (EShMessages)(messages | EShMsgAST);
TBuiltInResource resources;
GenerateResources(resources);
//
// Per-shader processing...
//
glslang::TProgram program;
std::string shaderName;
while (Worklist.remove(shaderName)) {
EShLanguage stage = FindLanguage(shaderName);
glslang::TShader* shader = new glslang::TShader(stage);
shaders.push_back(shader);
char** shaderStrings = ReadFileData(shaderName.c_str());
if (! shaderStrings) {
usage();
return;
}
shader->setStrings(shaderStrings, 1);
shader->parse(&resources, 100, false, messages);
program.addShader(shader);
if (! (Options & EOptionSuppressInfolog)) {
puts(shaderName.c_str());
puts(shader->getInfoLog());
puts(shader->getInfoDebugLog());
}
FreeFileData(shaderStrings);
}
//
// Program-level processing...
//
program.link(messages);
if (! (Options & EOptionSuppressInfolog)) {
puts(program.getInfoLog());
puts(program.getInfoDebugLog());
}
// free everything up
while (shaders.size() > 0) {
delete shaders.back();
shaders.pop_back();
}
// TODO: memory: for each compile, need a GetThreadPoolAllocator().pop();
}
int C_DECL main(int argc, char* argv[]) int C_DECL main(int argc, char* argv[])
{ {
bool compileFailed = false; bool compileFailed = false;
@ -205,6 +274,12 @@ int C_DECL main(int argc, char* argv[])
return EFailUsage; return EFailUsage;
} }
//
// Two modes:
// 1) linking all arguments together, single-threaded
// 2) independent arguments, can be tackled by multiple asynchronous threads, for testing thread safety
//
// TODO: finish threading, allow external control over number of threads // TODO: finish threading, allow external control over number of threads
const int NumThreads = 1; const int NumThreads = 1;
if (NumThreads > 1) { if (NumThreads > 1) {
@ -218,8 +293,12 @@ int C_DECL main(int argc, char* argv[])
} }
glslang::OS_WaitForAllThreads(threads, NumThreads); glslang::OS_WaitForAllThreads(threads, NumThreads);
} else { } else {
if (! CompileShaders(0)) if (Options & EOptionsLinkProgram) {
compileFailed = true; CompileAndLinkShaders();
} else {
if (! CompileShaders(0))
compileFailed = true;
}
} }
if (Delay) if (Delay)
@ -271,9 +350,10 @@ EShLanguage FindLanguage(const std::string& name)
} }
// //
// Read a file's data into a string, and compile it using ShCompile // Read a file's data into a string, and compile it using the old interface ShCompile,
// for non-linkable results.
// //
bool CompileFile(const char *fileName, ShHandle compiler, int Options, const TBuiltInResource *resources) bool CompileFile(const char *fileName, ShHandle compiler, int Options, const TBuiltInResource* resources)
{ {
int ret; int ret;
char** shaderStrings = ReadFileData(fileName); char** shaderStrings = ReadFileData(fileName);
@ -296,6 +376,7 @@ bool CompileFile(const char *fileName, ShHandle compiler, int Options, const TBu
messages = (EShMessages)(messages | EShMsgRelaxedErrors); messages = (EShMessages)(messages | EShMsgRelaxedErrors);
if (Options & EOptionIntermediate) if (Options & EOptionIntermediate)
messages = (EShMessages)(messages | EShMsgAST); messages = (EShMessages)(messages | EShMsgAST);
for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) { for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) {
for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) { for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) {
//ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, resources, Options, 100, false, messages); //ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, resources, Options, 100, false, messages);
@ -315,7 +396,6 @@ bool CompileFile(const char *fileName, ShHandle compiler, int Options, const TBu
return ret ? true : false; return ret ? true : false;
} }
// //
// print usage to stdout // print usage to stdout
// //
@ -429,16 +509,12 @@ char** ReadFileData(const char *fileName)
return return_data; return return_data;
} }
void FreeFileData(char **data) void FreeFileData(char **data)
{ {
for(int i=0;i<NumShaderStrings;i++) for(int i=0;i<NumShaderStrings;i++)
free(data[i]); free(data[i]);
} }
void InfoLogMsg(const char* msg, const char* name, const int num) void InfoLogMsg(const char* msg, const char* name, const int num)
{ {
printf(num >= 0 ? "#### %s %s %d INFO LOG ####\n" : printf(num >= 0 ? "#### %s %s %d INFO LOG ####\n" :

View File

@ -1,4 +1,4 @@
WARNING: 0:1: '#version' : statement missing: use #version on first line of shader WARNING: #version: statement missing; use #version on first line of shader
0:? Sequence 0:? Sequence
0:4 Sequence 0:4 Sequence
0:4 move second child to first child (highp float) 0:4 move second child to first child (highp float)

View File

@ -1,4 +1,4 @@
WARNING: 0:1: '#version' : statement missing: use #version on first line of shader WARNING: #version: statement missing; use #version on first line of shader
ERROR: 0:1: 'main' : function cannot take any parameter(s) ERROR: 0:1: 'main' : function cannot take any parameter(s)
ERROR: 0:1: 'int' : main function cannot return a value ERROR: 0:1: 'int' : main function cannot return a value
ERROR: 2 compilation errors. No code generated. ERROR: 2 compilation errors. No code generated.

View File

@ -0,0 +1,33 @@
mains1.frag
0:3Function Definition: main( (void)
0:3 Function Parameters:
mains2.frag
0:3Function Definition: main( (void)
0:3 Function Parameters:
noMain1.geom
ERROR: #version: geometry shaders require non-es profile and version 150 or above
ERROR: 1 compilation errors. No code generated.
0:3Function Definition: foo( (void)
0:3 Function Parameters:
noMain2.geom
0:3Function Definition: bar( (void)
0:3 Function Parameters:
Linked geometry stage:
ERROR: Missing entry point: Each stage requires one "void main()" entry point
Linked fragment stage:
ERROR: Too many entry points: Each stage can have at most one "void main()" entry point.

View File

@ -0,0 +1,26 @@
noMain.vert
0:3Function Definition: foo( (void)
0:3 Function Parameters:
mains.frag
ERROR: 0:7: 'main' : function already has a body
ERROR: 1 compilation errors. No code generated.
ERROR: node is still EOpNull!
0:3 Function Definition: main( (void)
0:3 Function Parameters:
0:7 Function Definition: main( (void)
0:7 Function Parameters:
Linked vertex stage:
ERROR: Missing entry point: Each stage requires one "void main()" entry point
Linked fragment stage:
ERROR: Too many entry points: Each stage can have at most one "void main()" entry point.

View File

@ -1,4 +1,4 @@
WARNING: 0:1: '#version' : statement missing: use #version on first line of shader WARNING: #version: statement missing; use #version on first line of shader
0:? Sequence 0:? Sequence
0:5 Function Definition: main( (void) 0:5 Function Definition: main( (void)
0:5 Function Parameters: 0:5 Function Parameters:

View File

@ -1,4 +1,4 @@
ERROR: 0:1: '#version' : statement must appear first in ESSL shader; before comments or newlines ERROR: #version: statement must appear first in es-profile shader; before comments or newlines
ERROR: 1 compilation errors. No code generated. ERROR: 1 compilation errors. No code generated.
ERROR: node is still EOpNull! ERROR: node is still EOpNull!

View File

@ -1,5 +1,4 @@
ERROR: #version: versions before 150 do not allow a profile token ERROR: #version: versions before 150 do not allow a profile token
ERROR: 0:1: '#version' : incorrect
ERROR: 0:38: 'attribute' : not supported in this stage: fragment ERROR: 0:38: 'attribute' : not supported in this stage: fragment
ERROR: 0:40: 'sampler2DRect' : Reserved word. ERROR: 0:40: 'sampler2DRect' : Reserved word.
ERROR: 0:40: 'rectangle texture' : not supported for this version or the enabled extensions ERROR: 0:40: 'rectangle texture' : not supported for this version or the enabled extensions

9
Test/mains.frag Normal file
View File

@ -0,0 +1,9 @@
#version 300 es
void main()
{
}
void main()
{
}

5
Test/mains1.frag Normal file
View File

@ -0,0 +1,5 @@
#version 110
void main()
{
}

5
Test/mains2.frag Normal file
View File

@ -0,0 +1,5 @@
#version 110
void main()
{
}

5
Test/noMain.vert Normal file
View File

@ -0,0 +1,5 @@
#version 300 es
void foo()
{
}

5
Test/noMain1.geom Normal file
View File

@ -0,0 +1,5 @@
#version 110
void foo()
{
}

5
Test/noMain2.geom Normal file
View File

@ -0,0 +1,5 @@
#version 150
void bar()
{
}

View File

@ -2,10 +2,28 @@
TARGETDIR=localResults TARGETDIR=localResults
BASEDIR=baseResults BASEDIR=baseResults
EXE=./glslangValidator.exe
#
# isolated compilation tests
#
while read t; do while read t; do
echo Running $t... echo Running $t...
b=`basename $t` b=`basename $t`
./glslangValidator.exe -i $t > $TARGETDIR/$b.out $EXE -i $t > $TARGETDIR/$b.out
diff -b $BASEDIR/$b.out $TARGETDIR/$b.out diff -b $BASEDIR/$b.out $TARGETDIR/$b.out
done < testlist done < testlist
#
# grouped shaders for link tests
#
function runLinkTest {
echo Running $*...
$EXE -i -l $* > $TARGETDIR/$1.out
diff -b $BASEDIR/$1.out $TARGETDIR/$1.out
}
runLinkTest mains1.frag mains2.frag noMain1.geom noMain2.geom
runLinkTest noMain.vert mains.frag

View File

@ -103,7 +103,7 @@ protected:
}; };
// //
// Link operations are base on a list of compile results... // Link operations are based on a list of compile results...
// //
typedef glslang::TVector<TCompiler*> TCompilerList; typedef glslang::TVector<TCompiler*> TCompilerList;
typedef glslang::TVector<TShHandleBase*> THandleList; typedef glslang::TVector<TShHandleBase*> THandleList;

View File

@ -1429,7 +1429,7 @@ void TBuiltIns::initialize(const TBuiltInResource &resources, int version, EProf
s.append("uniform gl_LightProducts gl_BackLightProduct[gl_MaxLights];"); s.append("uniform gl_LightProducts gl_BackLightProduct[gl_MaxLights];");
// //
// Textureg Environment and Generation, p. 152, p. 40-42. // Texture Environment and Generation, p. 152, p. 40-42.
// //
s.append("uniform vec4 gl_TextureEnvColor[gl_MaxTextureImageUnits];"); s.append("uniform vec4 gl_TextureEnvColor[gl_MaxTextureImageUnits];");
s.append("uniform vec4 gl_EyePlaneS[gl_MaxTextureCoords];"); s.append("uniform vec4 gl_EyePlaneS[gl_MaxTextureCoords];");

View File

@ -909,6 +909,30 @@ void TIntermediate::addSymbolLinkageNode(TIntermAggregate*& linkage, const TVari
linkage = growAggregate(linkage, node); linkage = growAggregate(linkage, node);
} }
//
// Merge the information in 'unit' into 'this'
//
void TIntermediate::merge(TIntermediate& unit)
{
numMains += unit.numMains;
}
void TIntermediate::errorCheck(TInfoSink& infoSink)
{
if (numMains < 1)
error(infoSink, "Missing entry point: Each stage requires one \"void main()\" entry point");
if (numMains > 1)
error(infoSink, "Too many entry points: Each stage can have at most one \"void main()\" entry point.");
}
void TIntermediate::error(TInfoSink& infoSink, const char* message)
{
infoSink.info.prefix(EPrefixError);
infoSink.info << message << "\n";
++numErrors;
}
// //
// This deletes the tree. // This deletes the tree.
// //

View File

@ -692,6 +692,7 @@ TIntermAggregate* TParseContext::handleFunctionPrototype(TSourceLoc loc, TFuncti
error(loc, "function cannot take any parameter(s)", function.getName().c_str(), ""); error(loc, "function cannot take any parameter(s)", function.getName().c_str(), "");
if (function.getReturnType().getBasicType() != EbtVoid) if (function.getReturnType().getBasicType() != EbtVoid)
error(loc, "", function.getReturnType().getCompleteTypeString().c_str(), "main function cannot return a value"); error(loc, "", function.getReturnType().getCompleteTypeString().c_str(), "main function cannot return a value");
intermediate.addMainCount();
} }
// //

View File

@ -164,6 +164,7 @@ public:
TScanContext* getScanContext() const { return scanContext; } TScanContext* getScanContext() const { return scanContext; }
void setPpContext(TPpContext* c) { ppContext = c; } void setPpContext(TPpContext* c) { ppContext = c; }
TPpContext* getPpContext() const { return ppContext; } TPpContext* getPpContext() const { return ppContext; }
void addError() { ++numErrors; }
int getNumErrors() const { return numErrors; } int getNumErrors() const { return numErrors; }
protected: protected:

View File

@ -103,7 +103,7 @@ TPoolAllocator* PerProcessGPA = 0;
bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink, bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink,
TSymbolTable& symbolTable) TSymbolTable& symbolTable)
{ {
TIntermediate intermediate(version, profile); TIntermediate intermediate(version, profile);
TParseContext parseContext(symbolTable, intermediate, true, version, profile, language, infoSink); TParseContext parseContext(symbolTable, intermediate, true, version, profile, language, infoSink);
TPpContext ppContext(parseContext); TPpContext ppContext(parseContext);
@ -262,16 +262,23 @@ void SetupBuiltinSymbolTable(int version, EProfile profile)
glslang::ReleaseGlobalLock(); glslang::ReleaseGlobalLock();
} }
bool DeduceProfile(TInfoSink& infoSink, int version, EProfile& profile) bool DeduceVersionProfile(TInfoSink& infoSink, EShLanguage stage, bool versionNotFirst, int defaultVersion, int& version, EProfile& profile)
{ {
const int FirstProfileVersion = 150; const int FirstProfileVersion = 150;
bool correct = true;
// Get a good version...
if (version == 0) {
version = defaultVersion;
infoSink.info.message(EPrefixWarning, "#version: statement missing; use #version on first line of shader");
}
// Get a good profile...
if (profile == ENoProfile) { if (profile == ENoProfile) {
if (version == 300) { if (version == 300) {
correct = false;
infoSink.info.message(EPrefixError, "#version: version 300 requires specifying the 'es' profile"); infoSink.info.message(EPrefixError, "#version: version 300 requires specifying the 'es' profile");
profile = EEsProfile; profile = EEsProfile;
return false;
} else if (version == 100) } else if (version == 100)
profile = EEsProfile; profile = EEsProfile;
else if (version >= FirstProfileVersion) else if (version >= FirstProfileVersion)
@ -281,39 +288,192 @@ bool DeduceProfile(TInfoSink& infoSink, int version, EProfile& profile)
} else { } else {
// a profile was provided... // a profile was provided...
if (version < 150) { if (version < 150) {
correct = false;
infoSink.info.message(EPrefixError, "#version: versions before 150 do not allow a profile token"); infoSink.info.message(EPrefixError, "#version: versions before 150 do not allow a profile token");
if (version == 100) if (version == 100)
profile = EEsProfile; profile = EEsProfile;
else else
profile = ENoProfile; profile = ENoProfile;
return false;
} else if (version == 300) { } else if (version == 300) {
if (profile != EEsProfile) { if (profile != EEsProfile) {
correct = false;
infoSink.info.message(EPrefixError, "#version: version 300 supports only the es profile"); infoSink.info.message(EPrefixError, "#version: version 300 supports only the es profile");
return false;
} }
profile = EEsProfile; profile = EEsProfile;
} else { } else {
if (profile == EEsProfile) { if (profile == EEsProfile) {
correct = false;
infoSink.info.message(EPrefixError, "#version: only version 300 supports the es profile"); infoSink.info.message(EPrefixError, "#version: only version 300 supports the es profile");
if (version >= FirstProfileVersion) if (version >= FirstProfileVersion)
profile = ECoreProfile; profile = ECoreProfile;
else else
profile = ENoProfile; profile = ENoProfile;
return false;
} }
// else: typical desktop case... e.g., "#version 410 core" // else: typical desktop case... e.g., "#version 410 core"
} }
} }
return true; // Correct for stage type...
switch (stage) {
case EShLangGeometry:
if (version < 150 || profile == EEsProfile) {
correct = false;
infoSink.info.message(EPrefixError, "#version: geometry shaders require non-es profile and version 150 or above");
version = 150;
if (profile == EEsProfile)
profile = ECoreProfile;
}
break;
case EShLangTessControl:
case EShLangTessEvaluation:
if (version < 400 || profile == EEsProfile) {
correct = false;
infoSink.info.message(EPrefixError, "#version: tessellation shaders require non-es profile and version 400 or above");
version = 400;
if (profile == EEsProfile)
profile = ECoreProfile;
}
break;
case EShLangCompute:
if (version < 430 || profile == EEsProfile) {
correct = false;
infoSink.info.message(EPrefixError, "#version: compute shaders require non-es profile and version 430 or above");
version = 430;
if (profile == EEsProfile)
profile = ECoreProfile;
}
break;
default:
break;
}
if (profile == EEsProfile && version >= 300 && versionNotFirst) {
correct = false;
infoSink.info.message(EPrefixError, "#version: statement must appear first in es-profile shader; before comments or newlines");
}
return correct;
}
//
// Do a partial compile on the given strings for a single compilation unit
// for a potential deferred link into a single stage (and deferred full compile of that
// stage through machine-dependent compilation).
//
// All preprocessing, parsing, semantic checks, etc. for a single compilation unit
// are done here.
//
// Return: The tree and other information is filled into the intermediate argument,
// and true is returned by the function for success.
//
bool CompileDeferred(
TCompiler* compiler,
const char* const shaderStrings[],
const int numStrings,
const int* inputLengths,
const EShOptimizationLevel optLevel,
const TBuiltInResource* resources,
int defaultVersion, // use 100 for ES environment, 110 for desktop
bool forwardCompatible, // give errors for use of deprecated features
EShMessages messages, // warnings/errors/AST; things to print out
TIntermediate& intermediate // returned tree, etc.
)
{
if (! InitThread())
return false;
if (numStrings == 0)
return true;
// This must be undone (.pop()) by the caller, after it finishes consuming the created tree.
GetThreadPoolAllocator().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;
glslang::TInputScanner input(numStrings, shaderStrings, lengths);
bool versionNotFirst = ScanVersion(input, version, profile);
bool goodVersion = DeduceVersionProfile(compiler->infoSink, compiler->getLanguage(), versionNotFirst, defaultVersion, version, profile);
intermediate.setVersion(version);
intermediate.setProfile(profile);
SetupBuiltinSymbolTable(version, profile);
TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
[profile]
[compiler->getLanguage()];
// Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
TSymbolTable* symbolTableMemory = new TSymbolTable;
TSymbolTable& symbolTable = *symbolTableMemory;
if (cachedTable)
symbolTable.adoptLevels(*cachedTable);
// Add built-in symbols that are potentially context dependent;
// they get popped again further down.
AddContextSpecificSymbols(resources, compiler->infoSink, symbolTable, version, profile, compiler->getLanguage());
TParseContext parseContext(symbolTable, intermediate, false, version, profile, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages);
glslang::TScanContext scanContext(parseContext);
TPpContext ppContext(parseContext);
parseContext.setScanContext(&scanContext);
parseContext.setPpContext(&ppContext);
if (! goodVersion)
parseContext.addError();
parseContext.initializeExtensionBehavior();
//
// Parse the application's shaders. All the following symbol table
// work will be throw-away, so push a new allocation scope that can
// be thrown away, then push a scope for the current shader's globals.
//
bool success = true;
symbolTable.push();
if (! symbolTable.atGlobalLevel())
parseContext.infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
if (parseContext.insertBuiltInArrayAtGlobalLevel())
success = false;
bool ret = parseContext.parseShaderStrings(ppContext, const_cast<char**>(shaderStrings), lengths, numStrings);
if (! ret)
success = false;
intermediate.addSymbolLinkageNodes(intermediate.getTreeRoot(), parseContext.linkage, parseContext.language, symbolTable);
// Clean up the symbol table. The AST is self-sufficient now.
delete symbolTableMemory;
if (success && intermediate.getTreeRoot()) {
if (optLevel == EShOptNoGeneration)
parseContext.infoSink.info.message(EPrefixNone, "No errors. No code generation or linking was requested.");
else
success = intermediate.postProcess(intermediate.getTreeRoot(), parseContext.language);
} else if (! success) {
parseContext.infoSink.info.prefix(EPrefixError);
parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n";
}
if (messages & EShMsgAST)
intermediate.outputTree(parseContext.infoSink);
delete [] lengths;
return success;
} }
} // end anonymous namespace for local functions } // end anonymous namespace for local functions
// //
// ShInitialize() should be called exactly once per process, not per thread. // ShInitialize() should be called exactly once per process, not per thread.
// //
@ -322,9 +482,8 @@ int ShInitialize()
if (! InitProcess()) if (! InitProcess())
return 0; return 0;
if (! PerProcessGPA) { if (! PerProcessGPA)
PerProcessGPA = new TPoolAllocator(); PerProcessGPA = new TPoolAllocator();
}
glslang::TScanContext::fillInKeywordMap(); glslang::TScanContext::fillInKeywordMap();
@ -400,8 +559,9 @@ int __fastcall ShFinalize()
} }
// //
// Do an actual compile on the given strings. The result is left // Do a full compile on the given strings for a single compilation unit
// in the given compile object. // forming a complete stage. The result of the machine dependent compilation
// is left in the provided compile object.
// //
// Return: The return value is really boolean, indicating // Return: The return value is really boolean, indicating
// success (1) or failure (0). // success (1) or failure (0).
@ -419,9 +579,7 @@ int ShCompile(
EShMessages messages // warnings/errors/AST; things to print out EShMessages messages // warnings/errors/AST; things to print out
) )
{ {
if (! InitThread()) // Map the generic handle to the C++ object
return 0;
if (handle == 0) if (handle == 0)
return 0; return 0;
@ -433,125 +591,26 @@ int ShCompile(
compiler->infoSink.info.erase(); compiler->infoSink.info.erase();
compiler->infoSink.debug.erase(); compiler->infoSink.debug.erase();
if (numStrings == 0) TIntermediate intermediate;
return 1; bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, optLevel, resources, defaultVersion, forwardCompatible, messages, intermediate);
GetThreadPoolAllocator().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;
glslang::TInputScanner input(numStrings, shaderStrings, lengths);
bool versionNotFirst = ScanVersion(input, version, profile);
if (version == 0) {
version = defaultVersion;
versionStatementMissing = true;
}
bool goodProfile = DeduceProfile(compiler->infoSink, version, profile);
TIntermediate intermediate(version, profile);
SetupBuiltinSymbolTable(version, profile);
TSymbolTable* cachedTable = SharedSymbolTables[MapVersionToIndex(version)]
[profile]
[compiler->getLanguage()];
// Dynamically allocate the symbol table so we can control when it is deallocated WRT the pool.
TSymbolTable* symbolTableMemory = new TSymbolTable;
TSymbolTable& symbolTable = *symbolTableMemory;
if (cachedTable)
symbolTable.adoptLevels(*cachedTable);
// Add built-in symbols that are potentially context dependent;
// they get popped again further down.
AddContextSpecificSymbols(resources, compiler->infoSink, symbolTable, version, profile, compiler->getLanguage());
TParseContext parseContext(symbolTable, intermediate, false, version, profile, compiler->getLanguage(), compiler->infoSink, forwardCompatible, messages);
glslang::TScanContext scanContext(parseContext);
TPpContext ppContext(parseContext);
parseContext.setScanContext(&scanContext);
parseContext.setPpContext(&ppContext);
TSourceLoc beginning;
beginning.line = 1;
beginning.string = 0;
if (! goodProfile)
parseContext.error(beginning, "incorrect", "#version", "");
if (versionStatementMissing)
parseContext.warn(beginning, "statement missing: use #version on first line of shader", "#version", "");
else if (profile == EEsProfile && version >= 300 && versionNotFirst)
parseContext.error(beginning, "statement must appear first in ESSL shader; before comments or newlines", "#version", "");
parseContext.initializeExtensionBehavior();
// //
// Parse the application's shaders. All the following symbol table // Call the machine dependent compiler
// work will be throw-away, so push a new allocation scope that can
// be thrown away, then push a scope for the current shader's globals.
// //
bool success = true; if (success && intermediate.getTreeRoot() && optLevel != EShOptNoGeneration)
success = compiler->compile(intermediate.getTreeRoot(), intermediate.getVersion(), intermediate.getProfile());
symbolTable.push();
if (! symbolTable.atGlobalLevel())
parseContext.infoSink.info.message(EPrefixInternalError, "Wrong symbol table level");
if (parseContext.insertBuiltInArrayAtGlobalLevel())
success = false;
bool ret = parseContext.parseShaderStrings(ppContext, const_cast<char**>(shaderStrings), lengths, numStrings);
if (! ret)
success = false;
intermediate.addSymbolLinkageNodes(intermediate.getTreeRoot(), parseContext.linkage, parseContext.language, symbolTable);
// Clean up the symbol table before deallocating the pool memory it used.
// The AST is self-sufficient now, so it can be done before the rest of compilation/linking.
delete symbolTableMemory;
if (success && intermediate.getTreeRoot()) {
if (optLevel == EShOptNoGeneration)
parseContext.infoSink.info.message(EPrefixNone, "No errors. No code generation or linking was requested.");
else {
success = intermediate.postProcess(intermediate.getTreeRoot(), parseContext.language);
if (success) {
//
// Call the machine dependent compiler
//
if (! compiler->compile(intermediate.getTreeRoot(), parseContext.version, parseContext.profile))
success = false;
}
}
} else if (! success) {
parseContext.infoSink.info.prefix(EPrefixError);
parseContext.infoSink.info << parseContext.getNumErrors() << " compilation errors. No code generated.\n\n";
success = false;
}
if (messages & EShMsgAST)
intermediate.outputTree(parseContext.infoSink);
intermediate.removeTree(); intermediate.removeTree();
//
// Throw away all the temporary memory used by the compilation process. // Throw away all the temporary memory used by the compilation process.
// // The push was done in the CompileDeferred() call above.
GetThreadPoolAllocator().pop(); GetThreadPoolAllocator().pop();
delete [] lengths;
return success ? 1 : 0; return success ? 1 : 0;
} }
// //
// Do an actual link on the given compile objects. // Link the given compile objects.
// //
// Return: The return value of is really boolean, indicating // Return: The return value of is really boolean, indicating
// success or failure. // success or failure.
@ -777,3 +836,135 @@ int ShGetUniformLocation(const ShHandle handle, const char* name)
return uniformMap->getLocation(name); return uniformMap->getLocation(name);
} }
////////////////////////////////////////////////////////////////////////////////////////////
//
// Deferred-Lowering C++ Interface
// -----------------------------------
//
// Below is a new alternate C++ interface that might potentially replace the above
// opaque handle-based interface.
//
// See more detailed comment in ShaderLang.h
//
namespace glslang {
class TDeferredCompiler : public TCompiler {
public:
TDeferredCompiler(EShLanguage s, TInfoSink& i) : TCompiler(s, i) { }
virtual bool compile(TIntermNode* root, int version = 0, EProfile profile = ENoProfile) { return true; }
};
TShader::TShader(EShLanguage s)
: stage(s)
{
infoSink = new TInfoSink;
compiler = new TDeferredCompiler(stage, *infoSink);
intermediate = new TIntermediate;
}
TShader::~TShader()
{
delete infoSink;
delete compiler;
delete intermediate;
}
//
// Turn the shader strings into a parse tree in the TIntermediate.
//
bool TShader::parse(const TBuiltInResource* builtInResources, int defaultVersion, bool forwardCompatible, EShMessages messages)
{
return CompileDeferred(compiler, strings, numStrings, 0, EShOptNone, builtInResources, defaultVersion, forwardCompatible, messages, *intermediate);
// TODO: memory: pool needs to be popped
}
const char* TShader::getInfoLog()
{
return infoSink->info.c_str();
}
const char* TShader::getInfoDebugLog()
{
return infoSink->debug.c_str();
}
TProgram::TProgram()
{
infoSink = new TInfoSink;
for (int s = 0; s < EShLangCount; ++s)
intermediate[s] = 0;
}
TProgram::~TProgram()
{
delete infoSink;
for (int s = 0; s < EShLangCount; ++s)
delete intermediate[s];
}
//
// Merge the compilation units within each stage into a single TIntermediate.
// All starting compilation units need to be the result of calling TShader::parse().
//
bool TProgram::link(EShMessages messages)
{
bool error = false;
for (int s = 0; s < EShLangCount; ++s) {
if (! linkStage((EShLanguage)s, messages))
error = true;
}
// TODO: Link: cross-stage error checking
return error;
}
bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
{
if (stages[stage].size() == 0)
return true;
//
// Be efficient for the common single compilation unit per stage case,
// reusing it's TIntermediate instead of merging into a new one.
//
TIntermediate* merged;
if (stages[stage].size() == 1)
merged = stages[stage].front()->intermediate;
else {
intermediate[stage] = new TIntermediate();
merged = intermediate[stage];
}
infoSink->info << "\nLinked " << StageName[stage] << " stage:\n\n";
if (stages[stage].size() > 1) {
std::list<TShader*>::const_iterator it;
for (it = stages[stage].begin(); it != stages[stage].end(); ++it)
merged->merge(*(*it)->intermediate);
if (messages & EShMsgAST)
merged->outputTree(*infoSink);
}
merged->errorCheck(*infoSink);
return merged->getNumErrors() > 0;
}
const char* TProgram::getInfoLog()
{
return infoSink->info.c_str();
}
const char* TProgram::getInfoDebugLog()
{
return infoSink->debug.c_str();
}
} // end namespace glslang

View File

@ -51,7 +51,8 @@ const char* StageName[EShLangCount] = {
"tessellation control", "tessellation control",
"tessellation evaluation", "tessellation evaluation",
"geometry", "geometry",
"fragment" "fragment",
"compute"
}; };
const char* ProfileName[EProfileCount] = { const char* ProfileName[EProfileCount] = {

View File

@ -56,10 +56,16 @@ class TVariable;
// //
class TIntermediate { class TIntermediate {
public: public:
TIntermediate(int v, EProfile p) : treeRoot(0), profile(p), version(v) { } explicit TIntermediate(int v = 0, EProfile p = ENoProfile) : treeRoot(0), profile(p), version(v), numMains(0), numErrors(0) { }
void setVersion(int v) { version = v; }
int getVersion() const { return version; }
void setProfile(EProfile p) { profile = p; }
EProfile getProfile() const { return profile; }
void setTreeRoot(TIntermNode* r) { treeRoot = r; } void setTreeRoot(TIntermNode* r) { treeRoot = r; }
TIntermNode* getTreeRoot() const { return treeRoot; } TIntermNode* getTreeRoot() const { return treeRoot; }
void addMainCount() { ++numMains; }
int getNumErrors() const { return numErrors; }
TIntermSymbol* addSymbol(int Id, const TString&, const TType&, TSourceLoc); TIntermSymbol* addSymbol(int Id, const TString&, const TType&, TSourceLoc);
TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*); TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*);
@ -93,13 +99,21 @@ public:
void addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable&, const TString&); void addSymbolLinkageNode(TIntermAggregate*& linkage, TSymbolTable&, const TString&);
void addSymbolLinkageNode(TIntermAggregate*& linkage, const TVariable&); void addSymbolLinkageNode(TIntermAggregate*& linkage, const TVariable&);
void merge(TIntermediate&);
void errorCheck(TInfoSink& infoSink);
void outputTree(TInfoSink& infoSink); void outputTree(TInfoSink& infoSink);
void removeTree(); void removeTree();
protected:
void error(TInfoSink& infoSink, const char*);
protected: protected:
TIntermNode* treeRoot; TIntermNode* treeRoot;
EProfile profile; EProfile profile;
int version; int version;
int numMains;
int numErrors;
private: private:
void operator=(TIntermediate&); // prevent assignments void operator=(TIntermediate&); // prevent assignments

View File

@ -76,26 +76,30 @@ SH_IMPORT_EXPORT int __fastcall ShFinalize();
// Types of languages the compiler can consume. // Types of languages the compiler can consume.
// //
typedef enum { typedef enum {
EShLangVertex, EShLangVertex,
EShLangTessControl, EShLangTessControl,
EShLangTessEvaluation, EShLangTessEvaluation,
EShLangGeometry, EShLangGeometry,
EShLangFragment, EShLangFragment,
EShLangCompute, EShLangCompute,
EShLangCount, EShLangCount,
} EShLanguage; } EShLanguage;
typedef enum { typedef enum {
EShLangVertexMask = (1 << EShLangVertex), EShLangVertexMask = (1 << EShLangVertex),
EShLangTessControlMask = (1 << EShLangTessControl), EShLangTessControlMask = (1 << EShLangTessControl),
EShLangTessEvaluationMask = (1 << EShLangTessEvaluation), EShLangTessEvaluationMask = (1 << EShLangTessEvaluation),
EShLangGeometryMask = (1 << EShLangGeometry), EShLangGeometryMask = (1 << EShLangGeometry),
EShLangFragmentMask = (1 << EShLangFragment), EShLangFragmentMask = (1 << EShLangFragment),
EShLangComputeMask = (1 << EShLangCompute), EShLangComputeMask = (1 << EShLangCompute),
} EShLanguageMask; } EShLanguageMask;
namespace glslang {
extern const char* StageName[EShLangCount]; extern const char* StageName[EShLangCount];
} // end namespace glslang
// //
// Types of output the linker will create. // Types of output the linker will create.
// //
@ -135,7 +139,7 @@ typedef struct {
typedef struct { typedef struct {
int numBindings; int numBindings;
ShBinding* bindings; // array of bindings ShBinding* bindings; // array of bindings
} ShBindingTable; } ShBindingTable;
// //
@ -220,13 +224,13 @@ SH_IMPORT_EXPORT int ShGetUniformLocation(const ShHandle uniformMap, const char*
// These are currently unused in the front end, but consumers of the front-end still // These are currently unused in the front end, but consumers of the front-end still
// be rely on them: // be rely on them:
enum TDebugOptions { enum TDebugOptions {
EDebugOpNone = 0x000, EDebugOpNone = 0x000,
EDebugOpIntermediate = 0x001, EDebugOpIntermediate = 0x001,
EDebugOpAssembly = 0x002, EDebugOpAssembly = 0x002,
EDebugOpObjectCode = 0x004, EDebugOpObjectCode = 0x004,
EDebugOpLinkMaps = 0x008, EDebugOpLinkMaps = 0x008,
EDebugOpSuppressInfolog = 0x010, EDebugOpSuppressInfolog = 0x010,
EDebugOpMemoryLeakMode = 0x020, EDebugOpMemoryLeakMode = 0x020,
EDebugOpTexturePrototypes = 0x040, EDebugOpTexturePrototypes = 0x040,
EDebugOpRelaxedErrors = 0x080, EDebugOpRelaxedErrors = 0x080,
EDebugOpGiveWarnings = 0x100, EDebugOpGiveWarnings = 0x100,
@ -236,4 +240,76 @@ enum TDebugOptions {
} // end extern "C" } // end extern "C"
#endif #endif
////////////////////////////////////////////////////////////////////////////////////////////
//
// Deferred-Lowering C++ Interface
// -----------------------------------
//
// Below is a new alternate C++ interface that might potentially replace the above
// opaque handle-based interface.
//
// The below is further designed to handle multiple compilation units per stage, where
// the intermediate results, including the parse tree, are preserved until link time,
// rather than the above interface which is designed to have each compilation unit
// lowered at compile time. In above model, linking occurs on the lowered results,
// whereas in this model intra-stage linking can occur at the parse tree
// (treeRoot in TIntermediate) level, and then a full stage can be lowered.
//
#include <list>
class TCompiler;
class TInfoSink;
namespace glslang {
class TIntermediate;
class TProgram;
class TShader {
public:
explicit TShader(EShLanguage);
virtual ~TShader();
void setStrings(char** s, int n) { strings = s; numStrings = n; }
bool parse(const TBuiltInResource*, int defaultVersion, bool forwardCompatible, EShMessages);
const char* getInfoLog();
const char* getInfoDebugLog();
protected:
EShLanguage stage;
TCompiler* compiler;
TIntermediate* intermediate;
TInfoSink* infoSink;
char** strings;
int numStrings;
friend class TProgram;
private:
void operator=(TShader&);
};
class TProgram {
public:
TProgram();
virtual ~TProgram();
void addShader(TShader* shader) { stages[shader->stage].push_back(shader); }
bool link(EShMessages);
const char* getInfoLog();
const char* getInfoDebugLog();
protected:
bool linkStage(EShLanguage, EShMessages);
protected:
std::list<TShader*> stages[EShLangCount];
TIntermediate* intermediate[EShLangCount];
TInfoSink* infoSink;
private:
void operator=(TProgram&);
};
} // end namespace glslang
#endif // _COMPILER_INTERFACE_INCLUDED_ #endif // _COMPILER_INTERFACE_INCLUDED_