Add and partially implement an interface for doing uniform reflection. It includes an AST traversal to identify live accesses.

It does not yet correctly compute block offsets, give correct GL-API-style type values, or handle arrays.

This is tied to the new -q flag.


git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@23938 e7fa87d3-cd2b-0410-9028-fcbf551c1848
This commit is contained in:
John Kessenich 2013-11-07 01:06:34 +00:00
parent 8ec55cdcd2
commit 11f9fc7247
15 changed files with 682 additions and 18 deletions

View File

@ -61,6 +61,7 @@ enum TOptions {
EOptionsLinkProgram = 0x020,
EOptionMultiThreaded = 0x040,
EOptionDumpConfig = 0x080,
EOptionDumpReflection = 0x100,
};
//
@ -466,6 +467,9 @@ bool ProcessArguments(int argc, char* argv[])
case 'm':
Options |= EOptionMemoryLeakMode;
break;
case 'q':
Options |= EOptionDumpReflection;
break;
case 'r':
Options |= EOptionRelaxedErrors;
break;
@ -575,6 +579,11 @@ void CompileAndLinkShaders()
puts(program.getInfoDebugLog());
}
if (Options & EOptionDumpReflection) {
program.buildReflection();
program.dumpReflection();
}
// Free everything up, program has to go before the shaders
// because it might have merged stuff from the shaders, and
// the stuff from the shaders has to have its destructors called
@ -771,6 +780,7 @@ void usage()
"-d: delay exit\n"
"-l: link validation of all input files\n"
"-m: memory leak mode\n"
"-q: dump reflection query database\n"
"-r: relaxed semantic error-checking mode\n"
"-s: silent mode\n"
"-t: multi-threaded mode\n");

View File

@ -0,0 +1,33 @@
reflection.vert
Warning, version 440 is not yet complete; some version-specific features are present, but many are missing.
Linked vertex stage:
Uniform reflection:
0:anonMember3: offset 32, type 35666, arraySize 1, index 0
1:s.a: offset -1, type 35666, arraySize 1, index -1
2:anonMember1: offset 0, type 35666, arraySize 1, index 0
3:uf1: offset -1, type 35666, arraySize 1, index -1
4:uf2: offset -1, type 35666, arraySize 1, index -1
5:ablock.member3: offset 32, type 35666, arraySize 1, index 1
Uniform block reflection:
0: nameless: offset -1, type -1, arraySize 1, index -1
1: ablock: offset -1, type -1, arraySize 1, index -1
Live names
ablock: 1
ablock.member3: 5
anonMember1: 2
anonMember3: 0
liveFunction1(: -1
liveFunction2(: -1
nameless: 0
s.a: 1
uf1: 3
uf2: 4

73
Test/reflection.vert Normal file
View File

@ -0,0 +1,73 @@
#version 440 core
uniform nameless {
vec3 anonMember1;
vec4 anonDeadMember2;
vec4 anonMember3;
};
uniform named {
vec3 deadMember1;
vec4 member2;
vec4 member3;
} ablock;
uniform namelessdead {
int a;
};
uniform namedDead {
int b;
} bblock;
struct TS {
int a;
int dead;
};
uniform TS s;
uniform float uf1;
uniform float uf2;
uniform float ufDead3;
uniform float ufDead4;
const bool control = true;
void deadFunction()
{
vec3 v3 = ablock.deadMember1;
vec4 v = anonDeadMember2;
float f = ufDead4;
}
void liveFunction2()
{
vec3 v = anonMember1;
float f = uf1;
}
void liveFunction1()
{
liveFunction2();
float f = uf2;
vec4 v = ablock.member3;
}
void main()
{
liveFunction1();
liveFunction2();
if (! control)
deadFunction();
float f;
if (control) {
liveFunction2();
f = anonMember3.z;
f = s.a;
} else
f = ufDead3;
}

View File

@ -43,6 +43,13 @@ runLinkTest 300link2.frag
runLinkTest 300link3.frag
runLinkTest empty.frag empty2.frag empty3.frag
#
# reflection tests
#
echo Running reflection...
$EXE -l -q reflection.vert > $TARGETDIR/reflection.vert.out
diff -b $BASEDIR/reflection.vert.out $TARGETDIR/reflection.vert.out
#
# multi-threaded test
#

View File

@ -36,6 +36,8 @@ Link Validation
- Non ES: write to only one of gl_FragColor, gl_FragData, or user-declared
- 1.50: at least one geometry shader says input primitive and at least one says output primitive...
- 1.50: at least one geometry shader says max_vertices...
- 1.50: geometry shaders: max_vertices must be checked against gl_MaxGeometryOutputVertices (maybe at compile time)
+ 1.50: origin_upper_left and pixel_center_integer have to match
- 4.4: An interface contains two different blocks, each with no instance name, where the blocks contain a member with the same name.
- 4.4: component aliasing (except desktop vertex shader inputs)
Intra-stage linking, multiple shader
@ -90,8 +92,8 @@ Shader Functionality to Implement/Finish
+ Add new minimum maximums for gl_MaxVertexOutputComponents, gl_MaxGeometryInputComponents, gl_MaxGeometryOutputComponents, and gl_MaxFragmentInputComponents,
rather than relying on gl_MaxVaryingComponents. Also, corrected gl_MaxVaryingComponents to be 60 instead of 64.
+ Added gl_PrimitiveID as an input to fragment shaders.
- Added gl_FragCoord qualifiers origin_upper_left, and pixel_center_integer to modify the values returned by gl_FragCoord (and have no affect on any other aspect of the pipeline or language).
- including redeclaration of gl_FragCoord that adds nothing
+ Added gl_FragCoord qualifiers origin_upper_left, and pixel_center_integer to modify the values returned by gl_FragCoord (and have no affect on any other aspect of the pipeline or language).
+ including redeclaration of gl_FragCoord that adds nothing
- Added support for multi-sample textures through sampler2DMS and sampler2DMSArray support in texelFetch() and textureSize().
+ Broadened interface blocks from just uniforms to in and out interfaces as well.
+ Broaden array usage to include vertex shader inputs (vertex in).

View File

@ -165,6 +165,7 @@ xcopy /y $(IntDir)$(TargetName)$(TargetExt) Test</Command>
<ClCompile Include="glslang\MachineIndependent\preprocessor\PpContext.cpp" />
<ClCompile Include="glslang\MachineIndependent\preprocessor\PpSymbols.cpp" />
<ClCompile Include="glslang\MachineIndependent\preprocessor\PpTokens.cpp" />
<ClCompile Include="glslang\MachineIndependent\reflection.cpp" />
<ClCompile Include="glslang\MachineIndependent\Scan.cpp" />
<ClCompile Include="glslang\MachineIndependent\Versions.cpp" />
<ClCompile Include="OGLCompilersDLL\InitializeDll.cpp" />
@ -192,6 +193,7 @@ xcopy /y $(IntDir)$(TargetName)$(TargetExt) Test</Command>
<ClInclude Include="glslang\MachineIndependent\ParseHelper.h" />
<ClInclude Include="glslang\MachineIndependent\preprocessor\PpContext.h" />
<ClInclude Include="glslang\MachineIndependent\preprocessor\PpTokens.h" />
<ClInclude Include="glslang\MachineIndependent\reflection.h" />
<ClInclude Include="glslang\MachineIndependent\RemoveTree.h" />
<ClInclude Include="glslang\MachineIndependent\localintermediate.h" />
<ClInclude Include="glslang\Include\BaseTypes.h" />

View File

@ -112,6 +112,9 @@
<ClCompile Include="glslang\MachineIndependent\linkValidate.cpp">
<Filter>Machine Independent</Filter>
</ClCompile>
<ClCompile Include="glslang\MachineIndependent\reflection.cpp">
<Filter>Machine Independent</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="glslang\MachineIndependent\Initialize.h">
@ -192,6 +195,9 @@
<ClInclude Include="glslang\MachineIndependent\preprocessor\PpTokens.h">
<Filter>Machine Independent\Preprocessor</Filter>
</ClInclude>
<ClInclude Include="glslang\MachineIndependent\reflection.h">
<Filter>Machine Independent</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="glslang\MachineIndependent\glslang.y">

View File

@ -588,7 +588,7 @@ public:
TIntermSelection(TIntermTyped* cond, TIntermNode* trueB, TIntermNode* falseB, const TType& type) :
TIntermTyped(type), condition(cond), trueBlock(trueB), falseBlock(falseB) {}
virtual void traverse(TIntermTraverser*);
virtual TIntermNode* getCondition() const { return condition; }
virtual TIntermTyped* getCondition() const { return condition; }
virtual TIntermNode* getTrueBlock() const { return trueBlock; }
virtual TIntermNode* getFalseBlock() const { return falseBlock; }
virtual TIntermSelection* getAsSelectionNode() { return this; }
@ -624,6 +624,12 @@ protected:
// When using this, just fill in the methods for nodes you want visited.
// Return false from a pre-visit to skip visiting that node's subtree.
//
// Explicitly set postVisit to true if you want post visiting, otherwise,
// filled in methods will only be called at pre-visit time (before processing
// the subtree).
//
// If you only want post-visits, explicitly turn off preVisit and turn on postVisit.
//
class TIntermTraverser {
public:
POOL_ALLOCATOR_NEW_DELETE(GetThreadPoolAllocator())

View File

@ -11,7 +11,7 @@ LIBCODEGEN=./../GenericCodeGen/libCodeGen.a
OBJECTS= Initialize.o IntermTraverse.o \
Intermediate.o ParseHelper.o PoolAlloc.o \
RemoveTree.o ShaderLang.o intermOut.o parseConst.o SymbolTable.o \
InfoSink.o Versions.o Constant.o Scan.o limits.o linkValidate.o
InfoSink.o Versions.o Constant.o Scan.o limits.o linkValidate.o reflection.o
SRCS= gen_glslang_tab.cpp Initialize.cpp IntermTraverse.cpp \
Intermediate.cpp ParseHelper.cpp PoolAlloc.cp \
@ -147,3 +147,4 @@ Versions.o: ParseHelper.h Versions.h ../Include/ShHandle.h SymbolTable.h locali
Constant.o: localintermediate.h ../Include/intermediate.h ../Public/ShaderLang.h SymbolTable.h Versions.h
limits.o: ParseHelper.h
linkValidate.o: localintermediate.h
reflection.o: ../Include/Common.h reflection.h localintermediate.h

View File

@ -53,6 +53,7 @@
#define SH_EXPORTING
#include "../Public/ShaderLang.h"
#include "reflection.h"
#include "Initialize.h"
namespace { // anonymous namespace for file-local functions and symbols
@ -967,18 +968,23 @@ const char* TShader::getInfoDebugLog()
return infoSink->debug.c_str();
}
TProgram::TProgram() : pool(0)
TProgram::TProgram() : pool(0), reflection(0), linked(false)
{
infoSink = new TInfoSink;
for (int s = 0; s < EShLangCount; ++s)
for (int s = 0; s < EShLangCount; ++s) {
intermediate[s] = 0;
newedIntermediate[s] = false;
}
}
TProgram::~TProgram()
{
delete infoSink;
delete reflection;
for (int s = 0; s < EShLangCount; ++s)
delete intermediate[s];
if (newedIntermediate[s])
delete intermediate[s];
delete pool;
}
@ -989,8 +995,12 @@ TProgram::~TProgram()
//
bool TProgram::link(EShMessages messages)
{
bool error = false;
if (linked)
return false;
linked = true;
bool error = false;
pool = new TPoolAllocator();
SetThreadPoolAllocator(*pool);
@ -1013,12 +1023,11 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
// 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;
intermediate[stage] = stages[stage].front()->intermediate;
else {
intermediate[stage] = new TIntermediate(stage);
merged = intermediate[stage];
newedIntermediate[stage] = true;
}
infoSink->info << "\nLinked " << StageName(stage) << " stage:\n\n";
@ -1026,15 +1035,15 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages)
if (stages[stage].size() > 1) {
std::list<TShader*>::const_iterator it;
for (it = stages[stage].begin(); it != stages[stage].end(); ++it)
merged->merge(*infoSink, *(*it)->intermediate);
intermediate[stage]->merge(*infoSink, *(*it)->intermediate);
if (messages & EShMsgAST)
merged->outputTree(*infoSink);
intermediate[stage]->outputTree(*infoSink);
}
merged->errorCheck(*infoSink);
intermediate[stage]->errorCheck(*infoSink);
return merged->getNumErrors() > 0;
return intermediate[stage]->getNumErrors() > 0;
}
const char* TProgram::getInfoLog()
@ -1047,4 +1056,38 @@ const char* TProgram::getInfoDebugLog()
return infoSink->debug.c_str();
}
//
// Reflection implementation.
//
bool TProgram::buildReflection()
{
if (! linked || reflection)
return false;
reflection = new TReflection;
for (int s = 0; s < EShLangCount; ++s) {
if (intermediate[s]) {
if (! reflection->addStage((EShLanguage)s, *intermediate[s]))
return false;
}
}
return true;
}
int TProgram::getNumLiveUniformVariables() { return reflection->getNumUniforms(); }
int TProgram::getNumLiveUniformBlocks() { return reflection->getNumUniformBlocks(); }
const char* TProgram::getUniformName(int index) { return reflection->getUniform(index).name.c_str(); }
const char* TProgram::getUniformBlockName(int index) { return reflection->getUniformBlock(index).name.c_str(); }
int TProgram::getUniformBlockSize(int index) { return reflection->getUniformBlock(index).size; }
int TProgram::getUniformIndex(const char* name) { return reflection->getIndex(name); }
int TProgram::getUniformBlockIndex(int index) { return reflection->getUniform(index).index; }
int TProgram::getUniformType(int index) { return reflection->getUniform(index).glDefineType; }
int TProgram::getUniformBufferOffset(int index) { return reflection->getUniform(index).offset; }
int TProgram::getUniformArraySize(int index) { return reflection->getUniform(index).size; }
void TProgram::dumpReflection() { reflection->dump(); }
} // end namespace glslang

View File

@ -296,7 +296,7 @@ void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink)
TCall* call = stack.back();
// Add to the stack just one callee.
// This algorithm always terminates, because only ! visited and ! currentPath causes a push
// This algorithm always terminates, because only !visited and !currentPath causes a push
// and all pushes change currentPath to true, and all pops change visited to true.
TGraph::iterator child = callGraph.begin();
for (; child != callGraph.end(); ++child) {
@ -312,6 +312,7 @@ void TIntermediate::checkCallGraphCycles(TInfoSink& infoSink)
error(infoSink, "Recursion detected:");
infoSink.info << " " << call->callee << " calling " << child->callee << "\n";
child->errorGiven = true;
recursive = true;
}
} else {
child->currentPath = true;

View File

@ -56,7 +56,8 @@ class TSymbol;
//
class TIntermediate {
public:
explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : language(l), treeRoot(0), profile(p), version(v), numMains(0), numErrors(0),
explicit TIntermediate(EShLanguage l, int v = 0, EProfile p = ENoProfile) : language(l), treeRoot(0), profile(p), version(v),
numMains(0), numErrors(0), recursive(false),
invocations(0), maxVertices(0), inputPrimitive(ElgNone), outputPrimitive(ElgNone), pixelCenterInteger(false), originUpperLeft(false) { }
void setVersion(int v) { version = v; }
@ -66,7 +67,9 @@ public:
void setTreeRoot(TIntermNode* r) { treeRoot = r; }
TIntermNode* getTreeRoot() const { return treeRoot; }
void addMainCount() { ++numMains; }
int getNumMains() const { return numMains; }
int getNumErrors() const { return numErrors; }
bool isRecursive() const { return recursive; }
TIntermSymbol* addSymbol(int Id, const TString&, const TType&, TSourceLoc);
TIntermTyped* addConversion(TOperator, const TType&, TIntermTyped*);
@ -157,6 +160,7 @@ protected:
int version;
int numMains;
int numErrors;
bool recursive;
int invocations;
int maxVertices;
TLayoutGeometry inputPrimitive;

View File

@ -0,0 +1,333 @@
//
//Copyright (C) 2013 LunarG, Inc.
//
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//POSSIBILITY OF SUCH DAMAGE.
//
#include "../Include/Common.h"
#include "reflection.h"
#include "localintermediate.h"
//
// Grow the reflection database through a friend traverser class of TReflection and a
// collection of functions to do a liveness traversal that note what uniforms are used
// in semantically non-dead code.
//
// Can be used multiple times, once per stage, to grow a program reflection.
//
// High-level algorithm for one stage:
//
// 1. Put main() on list of live functions.
//
// 2. Traverse any live function, while skipping if-tests with a compile-time constant
// condition of false, and while adding any encountered function calls to the live
// function list.
//
// Repeat until the live function list is empty.
//
// 3. Add any encountered uniform variables and blocks to the reflection database.
//
// Can be attempted with a failed link, but will return false if recursion had been detected, or
// there wasn't exactly one main.
//
namespace glslang {
//
// The traverser: mostly pass through, except
// - processing function-call nodes to push live functions onto the stack of functions to process
// - processing binary nodes to see if they are dereferences of aggregates to track
// - processing symbol nodes to see if they are non-aggregate objects to track
// - processing selection nodes to trim semantically dead code
//
// This is in the glslang namespace directly so it can be a friend of TReflection.
//
class TLiveTraverser : public TIntermTraverser {
public:
TLiveTraverser(const TIntermediate& i, TReflection& r) : intermediate(i), reflection(r) { }
// Track live funtions as well as uniforms, so that we don't visit dead functions
// and only visit each function once.
void addFunctionCall(TIntermAggregate* call)
{
// just use the map to ensure we process each function at most once
if (reflection.nameToIndex.find(call->getName()) == reflection.nameToIndex.end()) {
reflection.nameToIndex[call->getName()] = -1;
pushFunction(call->getName());
}
}
// Add a simple uniform variable reference to the uniform database, no derefence involved.
void addUniform(const TIntermSymbol& symbol)
{
if (reflection.nameToIndex.find(symbol.getName()) == reflection.nameToIndex.end()) {
if (isReflectionGranularity(symbol.getType())) {
reflection.nameToIndex[symbol.getName()] = reflection.indexToUniform.size();
reflection.indexToUniform.push_back(TObjectReflection(symbol.getName(), -1, MapToGlType(symbol.getType()), MapToGlArraySize(symbol.getType()), -1));
}
}
}
// Add a complex uniform reference where blocks/struct/arrays are involved in tha access.
void addDereferencedUniform(TIntermSymbol* base, TIntermBinary* node)
{
bool block = base->getBasicType() == EbtBlock;
int offset = -1;
int blockIndex = -1;
bool anonymous = false;
if (block) {
anonymous = base->getName().compare(0, 6, "__anon") == 0;
const TString& blockName = anonymous ? base->getType().getTypeName() : base->getName();
TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(blockName);
if (it == reflection.nameToIndex.end()) {
blockIndex = reflection.indexToUniformBlock.size();
reflection.nameToIndex[blockName] = blockIndex;
reflection.indexToUniformBlock.push_back(TObjectReflection(blockName, -1, -1, 1, -1));
} else
blockIndex = it->second;
}
TString name;
switch (node->getOp()) {
case EOpIndexDirect:
case EOpIndexIndirect:
// TODO: reflection: handle array dereferences
//name = base->getName();
//name.append("[]");
break;
case EOpIndexDirectStruct:
{
if (! anonymous) {
name = base->getName();
name.append(".");
}
int structIndex = node->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst();
if (block)
offset = structIndex * 16; // TODO: reflection: compute std140 offsets
name.append((*base->getType().getStruct())[structIndex].type->getFieldName().c_str());
break;
}
default:
break;
}
// TODO: reflection: handle deeper dereference chains than just one dereference
if (name.size() > 0) {
if (reflection.nameToIndex.find(name) == reflection.nameToIndex.end()) {
reflection.nameToIndex[name] = reflection.indexToUniform.size();
reflection.indexToUniform.push_back(TObjectReflection(name, offset, MapToGlType(node->getType()), MapToGlArraySize(node->getType()), blockIndex));
}
}
}
//
// Given a function name, find its subroot in the tree, and push it onto the stack of
// functions left to process.
//
void pushFunction(const TString& name)
{
TIntermSequence& globals = intermediate.getTreeRoot()->getAsAggregate()->getSequence();
for (unsigned int f = 0; f < globals.size(); ++f) {
TIntermAggregate* candidate = globals[f]->getAsAggregate();
if (candidate && candidate->getOp() == EOpFunction && candidate->getName() == name) {
functions.push_back(candidate);
break;
}
}
}
// Are we at a level in a dereference chain at which individual active uniform queries are made?
bool isReflectionGranularity(const TType& type)
{
return type.getBasicType() != EbtBlock && type.getBasicType() != EbtStruct;
}
// For a binary operation indexing into an aggregate, chase down the base of the aggregate.
// Return 0 if the topology does not fit this situation.
TIntermSymbol* findBase(const TIntermBinary* node)
{
TIntermSymbol *symbol = node->getLeft()->getAsSymbolNode();
if (symbol)
return symbol;
TIntermBinary* left = node->getLeft()->getAsBinaryNode();
if (! left)
return 0;
return findBase(left);
}
int MapToGlType(const TType& type)
{
// TODO: reflection: flesh out all GL types
#define GL_FLOAT_VEC4 0x8B52
return GL_FLOAT_VEC4;
}
int MapToGlArraySize(const TType& type)
{
return type.isArray() ? type.getArraySize() : 1;
}
typedef std::list<TIntermAggregate*> TFunctionStack;
TFunctionStack functions;
const TIntermediate& intermediate;
TReflection& reflection;
};
namespace {
//
// Implement the traversal functions of interest.
//
// To catch which function calls are not dead, and hence which functions must be visited.
bool LiveAggregate(bool /* preVisit */, TIntermAggregate* node, TIntermTraverser* it)
{
TLiveTraverser* oit = static_cast<TLiveTraverser*>(it);
if (node->getOp() == EOpFunctionCall)
oit->addFunctionCall(node);
return true; // traverse this subtree
}
// To catch dereferenced aggregates that must be reflected.
bool LiveBinary(bool /* preVisit */, TIntermBinary* node, TIntermTraverser* it)
{
TLiveTraverser* oit = static_cast<TLiveTraverser*>(it);
switch (node->getOp()) {
case EOpIndexDirect:
case EOpIndexIndirect:
case EOpIndexDirectStruct:
// If the left side is already small enough granularity to report, ignore
// this operation, and pick it up when the left side is visited.
if (! oit->isReflectionGranularity(node->getLeft()->getType()) &&
oit->isReflectionGranularity(node->getType())) {
// right granularity; see if this really is a uniform-based dereference
TIntermSymbol* base = oit->findBase(node);
if (base && base->getQualifier().storage == EvqUniform)
oit->addDereferencedUniform(base, node);
}
default:
break;
}
return true; // still need to visit everything below
}
// To catch non-dereferenced objects that must be reflected.
void LiveSymbol(TIntermSymbol* symbol, TIntermTraverser* it)
{
TLiveTraverser* oit = static_cast<TLiveTraverser*>(it);
if (symbol->getQualifier().storage == EvqUniform)
oit->addUniform(*symbol);
}
// To prune semantically dead paths.
bool LiveSelection(bool /* preVisit */, TIntermSelection* node, TIntermTraverser* it)
{
TLiveTraverser* oit = static_cast<TLiveTraverser*>(it);
TIntermConstantUnion* constant = node->getCondition()->getAsConstantUnion();
if (constant) {
// cull the path that is dead
if (constant->getConstArray()[0].getBConst() == true && node->getTrueBlock())
node->getTrueBlock()->traverse(it);
if (constant->getConstArray()[0].getBConst() == false && node->getFalseBlock())
node->getFalseBlock()->traverse(it);
return false; // don't traverse any more, we did it all above
} else
return true; // traverse the whole subtree
}
} // end anonymous namespace
//
// Implement TReflection methods.
//
// Merge live symbols from 'intermediate' into the existing reflection database.
//
// Returns false if the input is too malformed to do this.
bool TReflection::addStage(EShLanguage, const TIntermediate& intermediate)
{
if (intermediate.getNumMains() != 1 || intermediate.isRecursive())
return false;
TLiveTraverser it(intermediate, *this);
it.visitSymbol = LiveSymbol;
it.visitSelection = LiveSelection;
it.visitBinary = LiveBinary;
it.visitAggregate = LiveAggregate;
// put main() on functions to process
it.pushFunction("main(");
// process all the functions
while (! it.functions.empty()) {
TIntermNode* function = it.functions.back();
it.functions.pop_back();
function->traverse(&it);
}
return true;
}
void TReflection::dump()
{
printf("Uniform reflection:\n");
for (size_t i = 0; i < indexToUniform.size(); ++i) {
printf("%d:", i);
indexToUniform[i].dump();
}
printf("\n");
printf("Uniform block reflection:\n");
for (size_t i = 0; i < indexToUniformBlock.size(); ++i) {
printf("%d: ", i);
indexToUniformBlock[i].dump();
}
printf("\n");
printf("Live names\n");
for (TNameToIndex::const_iterator it = nameToIndex.begin(); it != nameToIndex.end(); ++it)
printf("%s: %d\n", it->first.c_str(), it->second);
printf("\n");
}
} // end namespace glslang

View File

@ -0,0 +1,122 @@
//
//Copyright (C) 2013 LunarG, Inc.
//
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//POSSIBILITY OF SUCH DAMAGE.
//
#ifndef _REFLECTION_INCLUDED
#define _REFLECTION_INCLUDED
#include "../Public/ShaderLang.h"
#include <list>
#include <set>
//
// A reflection database and its interface, consistent with the OpenGL API reflection queries.
//
namespace glslang {
class TIntermediate;
class TIntermAggregate;
class TLiveTraverser;
// Data needed for just a single object at the granularity exchanged by the reflection API
class TObjectReflection {
public:
TObjectReflection(const TString pName, int pOffset, int pGLDefineType, int pSize, int pIndex) :
name(pName), offset(pOffset), glDefineType(pGLDefineType), size(pSize), index(pIndex) { }
void dump() const { printf("%s: offset %d, type %d, arraySize %d, index %d\n", name.c_str(), offset, glDefineType, size, index); }
const TString name;
int offset;
int glDefineType;
int size; // data size in bytes for a block, array size for a (non-block) object that's an array
int index;
};
// The full reflection database
class TReflection {
public:
TReflection() : badReflection("__bad__", -1, -1, -1, -1) {}
virtual ~TReflection() {}
// grow the reflection stage by stage
bool addStage(EShLanguage, const TIntermediate&);
// for mapping a uniform index to a uniform object's description
int getNumUniforms() { return indexToUniform.size(); }
const TObjectReflection& getUniform(int i) const
{
if (i >= 0 && i < (int)indexToUniform.size())
return indexToUniform[i];
else
return badReflection;
}
// for mapping a block index to the block's description
int getNumUniformBlocks() const { return indexToUniformBlock.size(); }
const TObjectReflection& getUniformBlock(int i) const
{
if (i >= 0 && i < (int)indexToUniformBlock.size())
return indexToUniformBlock[i];
else
return badReflection;
}
// for mapping any name to its index (both block names and uniforms names)
int getIndex(const char* name) const
{
TNameToIndex::const_iterator it = nameToIndex.find(name);
if (it == nameToIndex.end())
return -1;
else
return it->second;
}
void dump();
protected:
friend glslang::TLiveTraverser;
typedef std::map<TString, int> TNameToIndex;
typedef std::vector<TObjectReflection> TMapIndexToReflection;
TObjectReflection badReflection; // return for queries of -1 or generally out of range; has expected descriptions with in it for this
TNameToIndex nameToIndex; // maps names to indexes; can hold all types of data: uniform/buffer and which function names have been processed
TMapIndexToReflection indexToUniform;
TMapIndexToReflection indexToUniformBlock;
};
} // end namespace glslang
#endif _REFLECTION_INCLUDED

View File

@ -305,6 +305,8 @@ private:
TShader& operator=(TShader&);
};
class TReflection;
// Make one TProgram per set of shaders that will get linked together. Add all
// the shaders that are to be linked together. After calling shader.parse()
// for all shaders, call link().
@ -316,17 +318,36 @@ public:
TProgram();
virtual ~TProgram();
void addShader(TShader* shader) { stages[shader->stage].push_back(shader); }
// Link Validation interface
bool link(EShMessages);
const char* getInfoLog();
const char* getInfoDebugLog();
// Reflection Interface
bool buildReflection(); // call first, to do liveness analysis, index mapping, etc.; returns false on failure
int getNumLiveUniformVariables(); // can be used for glGetProgramiv(GL_ACTIVE_UNIFORMS)
int getNumLiveUniformBlocks(); // can be used for glGetProgramiv(GL_ACTIVE_UNIFORM_BLOCKS)
const char* getUniformName(int index); // can be used for "name" part of glGetActiveUniform()
const char* getUniformBlockName(int index); // can be used for glGetActiveUniformBlockName()
int getUniformBlockSize(int index); // can be used for glGetActiveUniformBlockiv(UNIFORM_BLOCK_DATA_SIZE)
int getUniformIndex(const char* name); // can be used for glGetUniformIndices()
int getUniformBlockIndex(int index); // can be used for glGetActiveUniformsiv(GL_UNIFORM_BLOCK_INDEX)
int getUniformType(int index); // can be used for glGetActiveUniformsiv(GL_UNIFORM_TYPE)
int getUniformBufferOffset(int index); // can be used for glGetActiveUniformsiv(GL_UNIFORM_OFFSET)
int getUniformArraySize(int index); // can be used for glGetActiveUniformsiv(GL_UNIFORM_SIZE)
void dumpReflection();
protected:
bool linkStage(EShLanguage, EShMessages);
protected:
TPoolAllocator* pool;
std::list<TShader*> stages[EShLangCount];
TIntermediate* intermediate[EShLangCount];
bool newedIntermediate[EShLangCount]; // track which intermediate were "new" versus reusing a singleton unit in a stage
TInfoSink* infoSink;
TReflection* reflection;
bool linked;
private:
TProgram& operator=(TProgram&);