mirror of
https://github.com/libretro/glslang.git
synced 2024-11-27 09:51:24 +00:00
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
@ -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");
|
||||
|
33
Test/baseResults/reflection.vert.out
Normal file
33
Test/baseResults/reflection.vert.out
Normal 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
73
Test/reflection.vert
Normal 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;
|
||||
}
|
@ -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
|
||||
#
|
||||
|
6
Todo.txt
6
Todo.txt
@ -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).
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -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…
Reference in New Issue
Block a user