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:
parent
8ec55cdcd2
commit
11f9fc7247
15 changed files with 682 additions and 18 deletions
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
333
glslang/MachineIndependent/reflection.cpp
Normal file
333
glslang/MachineIndependent/reflection.cpp
Normal 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
|
||||
122
glslang/MachineIndependent/reflection.h
Normal file
122
glslang/MachineIndependent/reflection.h
Normal 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
|
||||
|
|
@ -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&);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue