Commit 11f9fc72 by John Kessenich

Add and partially implement an interface for doing uniform reflection. It…

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
parent 8ec55cdc
......@@ -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");
......
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
#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;
}
......@@ -44,6 +44,13 @@ 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
#
echo Comparing single thread to multithread for all tests in current directory...
......
......@@ -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;
......
//
//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&);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment