mirror of
https://github.com/KhronosGroup/glslang
synced 2024-11-08 11:30:06 +00:00
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:
parent
2f1eb37d82
commit
69f4b517c2
@ -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" :
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
33
Test/baseResults/mains1.frag.out
Normal file
33
Test/baseResults/mains1.frag.out
Normal 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.
|
||||||
|
|
||||||
|
|
26
Test/baseResults/noMain.vert.out
Normal file
26
Test/baseResults/noMain.vert.out
Normal 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.
|
||||||
|
|
||||||
|
|
@ -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:
|
||||||
|
@ -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!
|
||||||
|
@ -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
9
Test/mains.frag
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#version 300 es
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
}
|
5
Test/mains1.frag
Normal file
5
Test/mains1.frag
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#version 110
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
}
|
5
Test/mains2.frag
Normal file
5
Test/mains2.frag
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#version 110
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
}
|
5
Test/noMain.vert
Normal file
5
Test/noMain.vert
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#version 300 es
|
||||||
|
|
||||||
|
void foo()
|
||||||
|
{
|
||||||
|
}
|
5
Test/noMain1.geom
Normal file
5
Test/noMain1.geom
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#version 110
|
||||||
|
|
||||||
|
void foo()
|
||||||
|
{
|
||||||
|
}
|
5
Test/noMain2.geom
Normal file
5
Test/noMain2.geom
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#version 150
|
||||||
|
|
||||||
|
void bar()
|
||||||
|
{
|
||||||
|
}
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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];");
|
||||||
|
@ -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.
|
||||||
//
|
//
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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] = {
|
||||||
|
@ -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
|
||||||
|
@ -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_
|
||||||
|
Loading…
Reference in New Issue
Block a user