return true;
}
-// Thread entry point
+// Thread entry point, for non-linking asynchronous mode.
unsigned int
#ifdef _WIN32
__stdcall
#endif
CompileShaders(void*)
{
- ShHandle compiler;
-
std::string shaderName;
while (Worklist.remove(shaderName)) {
- compiler = ShConstructCompiler(FindLanguage(shaderName), Options);
+ ShHandle compiler = ShConstructCompiler(FindLanguage(shaderName), Options);
if (compiler == 0)
return false;
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[])
{
bool compileFailed = false;
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
const int NumThreads = 1;
if (NumThreads > 1) {
}
glslang::OS_WaitForAllThreads(threads, NumThreads);
} else {
- if (! CompileShaders(0))
- compileFailed = true;
+ if (Options & EOptionsLinkProgram) {
+ CompileAndLinkShaders();
+ } else {
+ if (! CompileShaders(0))
+ compileFailed = true;
+ }
}
if (Delay)
}
//
-// 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;
char** shaderStrings = ReadFileData(fileName);
messages = (EShMessages)(messages | EShMsgRelaxedErrors);
if (Options & EOptionIntermediate)
messages = (EShMessages)(messages | EShMsgAST);
+
for (int i = 0; i < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++i) {
for (int j = 0; j < ((Options & EOptionMemoryLeakMode) ? 100 : 1); ++j) {
//ret = ShCompile(compiler, shaderStrings, NumShaderStrings, lengths, EShOptNone, resources, Options, 100, false, messages);
return ret ? true : false;
}
-
//
// print usage to stdout
//
return return_data;
}
-
-
void FreeFileData(char **data)
{
for(int i=0;i<NumShaderStrings;i++)
free(data[i]);
}
-
-
void InfoLogMsg(const char* msg, const char* name, const int num)
{
printf(num >= 0 ? "#### %s %s %d INFO LOG ####\n" :
bool InitializeSymbolTable(const TString& builtIns, int version, EProfile profile, EShLanguage language, TInfoSink& infoSink,
TSymbolTable& symbolTable)
{
- TIntermediate intermediate(version, profile);
+ TIntermediate intermediate(version, profile);
TParseContext parseContext(symbolTable, intermediate, true, version, profile, language, infoSink);
TPpContext ppContext(parseContext);
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;
+ 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 (version == 300) {
+ correct = false;
infoSink.info.message(EPrefixError, "#version: version 300 requires specifying the 'es' profile");
profile = EEsProfile;
-
- return false;
} else if (version == 100)
profile = EEsProfile;
else if (version >= FirstProfileVersion)
} else {
// a profile was provided...
if (version < 150) {
+ correct = false;
infoSink.info.message(EPrefixError, "#version: versions before 150 do not allow a profile token");
if (version == 100)
profile = EEsProfile;
else
profile = ENoProfile;
-
- return false;
} else if (version == 300) {
if (profile != EEsProfile) {
+ correct = false;
infoSink.info.message(EPrefixError, "#version: version 300 supports only the es profile");
-
- return false;
}
profile = EEsProfile;
} else {
if (profile == EEsProfile) {
+ correct = false;
infoSink.info.message(EPrefixError, "#version: only version 300 supports the es profile");
if (version >= FirstProfileVersion)
profile = ECoreProfile;
else
profile = ENoProfile;
-
- return false;
}
// 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
+
//
// ShInitialize() should be called exactly once per process, not per thread.
//
if (! InitProcess())
return 0;
- if (! PerProcessGPA) {
+ if (! PerProcessGPA)
PerProcessGPA = new TPoolAllocator();
- }
glslang::TScanContext::fillInKeywordMap();
}
//
-// Do an actual compile on the given strings. The result is left
-// in the given compile object.
+// Do a full compile on the given strings for a single compilation unit
+// 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
// success (1) or failure (0).
EShMessages messages // warnings/errors/AST; things to print out
)
{
- if (! InitThread())
- return 0;
-
+ // Map the generic handle to the C++ object
if (handle == 0)
return 0;
compiler->infoSink.info.erase();
compiler->infoSink.debug.erase();
- if (numStrings == 0)
- return 1;
-
- 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();
+ TIntermediate intermediate;
+ bool success = CompileDeferred(compiler, shaderStrings, numStrings, inputLengths, optLevel, resources, defaultVersion, forwardCompatible, messages, intermediate);
//
- // 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.
+ // Call the machine dependent compiler
//
- 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 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);
+ if (success && intermediate.getTreeRoot() && optLevel != EShOptNoGeneration)
+ success = compiler->compile(intermediate.getTreeRoot(), intermediate.getVersion(), intermediate.getProfile());
intermediate.removeTree();
- //
+
// Throw away all the temporary memory used by the compilation process.
- //
+ // The push was done in the CompileDeferred() call above.
GetThreadPoolAllocator().pop();
- delete [] lengths;
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
// success or failure.
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
// Types of languages the compiler can consume.
//
typedef enum {
- EShLangVertex,
+ EShLangVertex,
EShLangTessControl,
EShLangTessEvaluation,
EShLangGeometry,
- EShLangFragment,
+ EShLangFragment,
EShLangCompute,
EShLangCount,
} EShLanguage;
typedef enum {
- EShLangVertexMask = (1 << EShLangVertex),
+ EShLangVertexMask = (1 << EShLangVertex),
EShLangTessControlMask = (1 << EShLangTessControl),
EShLangTessEvaluationMask = (1 << EShLangTessEvaluation),
EShLangGeometryMask = (1 << EShLangGeometry),
- EShLangFragmentMask = (1 << EShLangFragment),
- EShLangComputeMask = (1 << EShLangCompute),
+ EShLangFragmentMask = (1 << EShLangFragment),
+ EShLangComputeMask = (1 << EShLangCompute),
} EShLanguageMask;
+namespace glslang {
+
extern const char* StageName[EShLangCount];
+} // end namespace glslang
+
//
// Types of output the linker will create.
//
typedef struct {
int numBindings;
- ShBinding* bindings; // array of bindings
+ ShBinding* bindings; // array of bindings
} ShBindingTable;
//
// These are currently unused in the front end, but consumers of the front-end still
// be rely on them:
enum TDebugOptions {
- EDebugOpNone = 0x000,
- EDebugOpIntermediate = 0x001,
- EDebugOpAssembly = 0x002,
+ EDebugOpNone = 0x000,
+ EDebugOpIntermediate = 0x001,
+ EDebugOpAssembly = 0x002,
EDebugOpObjectCode = 0x004,
- EDebugOpLinkMaps = 0x008,
- EDebugOpSuppressInfolog = 0x010,
- EDebugOpMemoryLeakMode = 0x020,
+ EDebugOpLinkMaps = 0x008,
+ EDebugOpSuppressInfolog = 0x010,
+ EDebugOpMemoryLeakMode = 0x020,
EDebugOpTexturePrototypes = 0x040,
EDebugOpRelaxedErrors = 0x080,
EDebugOpGiveWarnings = 0x100,
} // end extern "C"
#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_