Commit 966456de by Olli Etuaho Committed by Commit Bot

Remove SH_TIMING_RESTRICTIONS compiler flag

The timing restrictions code is not in use and not updated for ESSL3, so it is better to remove it to make refactoring the AST easier. It can also be argued that perfect prevention of shader timing attacks is not feasible due to factors that are not under control of ANGLE, such as fixed function color compression in GPUs. Such color compression may make the use of texture bandwidth and thus performance dependent on the content of a texture regardless of whether a compressed format is chosen through the API. SH_DEPENDENCY_GRAPH flag that could only be active together with the timing restrictions flag is also removed, along with all the code that was supporting it. The newer CallDAG code is used for different purposes and is kept. BUG=angleproject:1490 TEST=angle_unittests Change-Id: I2cd10e18df366e8e43f7c3af1ca12d2a4bfb2007 Reviewed-on: https://chromium-review.googlesource.com/384511 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent e79c2d1f
...@@ -49,7 +49,7 @@ typedef unsigned int GLenum; ...@@ -49,7 +49,7 @@ typedef unsigned int GLenum;
// Version number for shader translation API. // Version number for shader translation API.
// It is incremented every time the API changes. // It is incremented every time the API changes.
#define ANGLE_SH_VERSION 158 #define ANGLE_SH_VERSION 159
typedef enum { typedef enum {
SH_GLES2_SPEC, SH_GLES2_SPEC,
...@@ -114,22 +114,6 @@ typedef enum { ...@@ -114,22 +114,6 @@ typedef enum {
// i is an integer. // i is an integer.
SH_EMULATE_ABS_INT_FUNCTION = 0x0100, SH_EMULATE_ABS_INT_FUNCTION = 0x0100,
// This is an experimental flag to enforce restrictions that aim to prevent
// timing attacks.
// It generates compilation errors for shaders that could expose sensitive
// texture information via the timing channel.
// To use this flag, you must compile the shader under the WebGL spec
// (using the SH_WEBGL_SPEC flag).
SH_TIMING_RESTRICTIONS = 0x0200,
// This flag prints the dependency graph that is used to enforce timing
// restrictions on fragment shaders.
// This flag only has an effect if all of the following are true:
// - The shader spec is SH_WEBGL_SPEC.
// - The compile options contain the SH_TIMING_RESTRICTIONS flag.
// - The shader type is GL_FRAGMENT_SHADER.
SH_DEPENDENCY_GRAPH = 0x0400,
// Enforce the GLSL 1.017 Appendix A section 7 packing restrictions. // Enforce the GLSL 1.017 Appendix A section 7 packing restrictions.
// This flag only enforces (and can only enforce) the packing // This flag only enforces (and can only enforce) the packing
// restrictions for uniform variables in both vertex and fragment // restrictions for uniform variables in both vertex and fragment
......
...@@ -94,8 +94,6 @@ int main(int argc, char *argv[]) ...@@ -94,8 +94,6 @@ int main(int argc, char *argv[])
case 'o': compileOptions |= SH_OBJECT_CODE; break; case 'o': compileOptions |= SH_OBJECT_CODE; break;
case 'u': compileOptions |= SH_VARIABLES; break; case 'u': compileOptions |= SH_VARIABLES; break;
case 'l': compileOptions |= SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX; break; case 'l': compileOptions |= SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX; break;
case 'd': compileOptions |= SH_DEPENDENCY_GRAPH; break;
case 't': compileOptions |= SH_TIMING_RESTRICTIONS; break;
case 'p': resources.WEBGL_debug_shader_precision = 1; break; case 'p': resources.WEBGL_debug_shader_precision = 1; break;
case 's': case 's':
if (argv[0][2] == '=') if (argv[0][2] == '=')
...@@ -316,14 +314,12 @@ void usage() ...@@ -316,14 +314,12 @@ void usage()
{ {
// clang-format off // clang-format off
printf( printf(
"Usage: translate [-i -o -u -l -t -d -p -b=e -b=g -b=h9 -x=i -x=d] file1 file2 ...\n" "Usage: translate [-i -o -u -l -p -b=e -b=g -b=h9 -x=i -x=d] file1 file2 ...\n"
"Where: filename : filename ending in .frag or .vert\n" "Where: filename : filename ending in .frag or .vert\n"
" -i : print intermediate tree\n" " -i : print intermediate tree\n"
" -o : print translated code\n" " -o : print translated code\n"
" -u : print active attribs, uniforms, varyings and program outputs\n" " -u : print active attribs, uniforms, varyings and program outputs\n"
" -l : unroll for-loops with integer indices\n" " -l : unroll for-loops with integer indices\n"
" -t : enforce experimental timing restrictions\n"
" -d : print dependency graph used to enforce timing restrictions\n"
" -p : use precision emulation\n" " -p : use precision emulation\n"
" -s=e2 : use GLES2 spec (this is by default)\n" " -s=e2 : use GLES2 spec (this is by default)\n"
" -s=e3 : use GLES3 spec (in development)\n" " -s=e3 : use GLES3 spec (in development)\n"
......
...@@ -121,13 +121,6 @@ ...@@ -121,13 +121,6 @@
'compiler/translator/VariablePacker.h', 'compiler/translator/VariablePacker.h',
'compiler/translator/blocklayout.cpp', 'compiler/translator/blocklayout.cpp',
'compiler/translator/blocklayout.h', 'compiler/translator/blocklayout.h',
'compiler/translator/depgraph/DependencyGraph.cpp',
'compiler/translator/depgraph/DependencyGraph.h',
'compiler/translator/depgraph/DependencyGraphBuilder.cpp',
'compiler/translator/depgraph/DependencyGraphBuilder.h',
'compiler/translator/depgraph/DependencyGraphOutput.cpp',
'compiler/translator/depgraph/DependencyGraphOutput.h',
'compiler/translator/depgraph/DependencyGraphTraverse.cpp',
'compiler/translator/glslang.h', 'compiler/translator/glslang.h',
'compiler/translator/glslang.l', 'compiler/translator/glslang.l',
'compiler/translator/glslang.y', 'compiler/translator/glslang.y',
...@@ -136,10 +129,6 @@ ...@@ -136,10 +129,6 @@
'compiler/translator/glslang_tab.h', 'compiler/translator/glslang_tab.h',
'compiler/translator/intermOut.cpp', 'compiler/translator/intermOut.cpp',
'compiler/translator/length_limits.h', 'compiler/translator/length_limits.h',
'compiler/translator/timing/RestrictFragmentShaderTiming.cpp',
'compiler/translator/timing/RestrictFragmentShaderTiming.h',
'compiler/translator/timing/RestrictVertexShaderTiming.cpp',
'compiler/translator/timing/RestrictVertexShaderTiming.h',
'compiler/translator/util.cpp', 'compiler/translator/util.cpp',
'compiler/translator/util.h', 'compiler/translator/util.h',
'third_party/compiler/ArrayBoundsClamper.cpp', 'third_party/compiler/ArrayBoundsClamper.cpp',
......
...@@ -24,10 +24,6 @@ ...@@ -24,10 +24,6 @@
#include "compiler/translator/ValidateMaxParameters.h" #include "compiler/translator/ValidateMaxParameters.h"
#include "compiler/translator/ValidateOutputs.h" #include "compiler/translator/ValidateOutputs.h"
#include "compiler/translator/VariablePacker.h" #include "compiler/translator/VariablePacker.h"
#include "compiler/translator/depgraph/DependencyGraph.h"
#include "compiler/translator/depgraph/DependencyGraphOutput.h"
#include "compiler/translator/timing/RestrictFragmentShaderTiming.h"
#include "compiler/translator/timing/RestrictVertexShaderTiming.h"
#include "third_party/compiler/ArrayBoundsClamper.h" #include "third_party/compiler/ArrayBoundsClamper.h"
#include "angle_gl.h" #include "angle_gl.h"
#include "common/utilities.h" #include "common/utilities.h"
...@@ -290,9 +286,6 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[], ...@@ -290,9 +286,6 @@ TIntermNode *TCompiler::compileTreeImpl(const char *const shaderStrings[],
if (success && shouldRunLoopAndIndexingValidation(compileOptions)) if (success && shouldRunLoopAndIndexingValidation(compileOptions))
success = validateLimitations(root); success = validateLimitations(root);
if (success && (compileOptions & SH_TIMING_RESTRICTIONS))
success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
// Unroll for-loop markup needs to happen after validateLimitations pass. // Unroll for-loop markup needs to happen after validateLimitations pass.
if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX)) if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
{ {
...@@ -759,36 +752,6 @@ bool TCompiler::validateLimitations(TIntermNode* root) ...@@ -759,36 +752,6 @@ bool TCompiler::validateLimitations(TIntermNode* root)
return validate.numErrors() == 0; return validate.numErrors() == 0;
} }
bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
{
if (shaderSpec != SH_WEBGL_SPEC)
{
infoSink.info << "Timing restrictions must be enforced under the WebGL spec.";
return false;
}
if (shaderType == GL_FRAGMENT_SHADER)
{
TDependencyGraph graph(root);
// Output any errors first.
bool success = enforceFragmentShaderTimingRestrictions(graph);
// Then, output the dependency graph.
if (outputGraph)
{
TDependencyGraphOutput output(infoSink.info);
output.outputAllSpanningTrees(graph);
}
return success;
}
else
{
return enforceVertexShaderTimingRestrictions(root);
}
}
bool TCompiler::limitExpressionComplexity(TIntermNode* root) bool TCompiler::limitExpressionComplexity(TIntermNode* root)
{ {
TMaxDepthTraverser traverser(maxExpressionComplexity+1); TMaxDepthTraverser traverser(maxExpressionComplexity+1);
...@@ -809,20 +772,6 @@ bool TCompiler::limitExpressionComplexity(TIntermNode* root) ...@@ -809,20 +772,6 @@ bool TCompiler::limitExpressionComplexity(TIntermNode* root)
return true; return true;
} }
bool TCompiler::enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph)
{
RestrictFragmentShaderTiming restrictor(infoSink.info);
restrictor.enforceRestrictions(graph);
return restrictor.numErrors() == 0;
}
bool TCompiler::enforceVertexShaderTimingRestrictions(TIntermNode* root)
{
RestrictVertexShaderTiming restrictor(infoSink.info);
restrictor.enforceRestrictions(root);
return restrictor.numErrors() == 0;
}
void TCompiler::collectVariables(TIntermNode* root) void TCompiler::collectVariables(TIntermNode* root)
{ {
if (!variablesCollected) if (!variablesCollected)
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include "third_party/compiler/ArrayBoundsClamper.h" #include "third_party/compiler/ArrayBoundsClamper.h"
class TCompiler; class TCompiler;
class TDependencyGraph;
#ifdef ANGLE_ENABLE_HLSL #ifdef ANGLE_ENABLE_HLSL
class TranslatorHLSL; class TranslatorHLSL;
#endif // ANGLE_ENABLE_HLSL #endif // ANGLE_ENABLE_HLSL
...@@ -138,13 +137,6 @@ class TCompiler : public TShHandleBase ...@@ -138,13 +137,6 @@ class TCompiler : public TShHandleBase
// while spec says it is allowed. // while spec says it is allowed.
// This function should only be applied to vertex shaders. // This function should only be applied to vertex shaders.
void initializeGLPosition(TIntermNode* root); void initializeGLPosition(TIntermNode* root);
// Returns true if the shader passes the restrictions that aim to prevent timing attacks.
bool enforceTimingRestrictions(TIntermNode* root, bool outputGraph);
// Returns true if the shader does not use samplers.
bool enforceVertexShaderTimingRestrictions(TIntermNode* root);
// Returns true if the shader does not use sampler dependent values to affect control
// flow or in operations whose time can depend on the input values.
bool enforceFragmentShaderTimingRestrictions(const TDependencyGraph& graph);
// Return true if the maximum expression complexity is below the limit. // Return true if the maximum expression complexity is below the limit.
bool limitExpressionComplexity(TIntermNode* root); bool limitExpressionComplexity(TIntermNode* root);
// Get built-in extensions with default behavior. // Get built-in extensions with default behavior.
......
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/depgraph/DependencyGraph.h"
#include "compiler/translator/depgraph/DependencyGraphBuilder.h"
TDependencyGraph::TDependencyGraph(TIntermNode* intermNode)
{
TDependencyGraphBuilder::build(intermNode, this);
}
TDependencyGraph::~TDependencyGraph()
{
for (TGraphNodeVector::const_iterator iter = mAllNodes.begin(); iter != mAllNodes.end(); ++iter)
{
TGraphNode* node = *iter;
delete node;
}
}
TGraphArgument* TDependencyGraph::createArgument(TIntermAggregate* intermFunctionCall,
int argumentNumber)
{
TGraphArgument* argument = new TGraphArgument(intermFunctionCall, argumentNumber);
mAllNodes.push_back(argument);
return argument;
}
TGraphFunctionCall* TDependencyGraph::createFunctionCall(TIntermAggregate* intermFunctionCall)
{
TGraphFunctionCall* functionCall = new TGraphFunctionCall(intermFunctionCall);
mAllNodes.push_back(functionCall);
if (functionCall->getIntermFunctionCall()->isUserDefined())
mUserDefinedFunctionCalls.push_back(functionCall);
return functionCall;
}
TGraphSymbol* TDependencyGraph::getOrCreateSymbol(TIntermSymbol* intermSymbol)
{
TSymbolIdMap::const_iterator iter = mSymbolIdMap.find(intermSymbol->getId());
TGraphSymbol* symbol = NULL;
if (iter != mSymbolIdMap.end()) {
TSymbolIdPair pair = *iter;
symbol = pair.second;
} else {
symbol = new TGraphSymbol(intermSymbol);
mAllNodes.push_back(symbol);
TSymbolIdPair pair(intermSymbol->getId(), symbol);
mSymbolIdMap.insert(pair);
// We save all sampler symbols in a collection, so we can start graph traversals from them quickly.
if (IsSampler(intermSymbol->getBasicType()))
mSamplerSymbols.push_back(symbol);
}
return symbol;
}
TGraphSelection* TDependencyGraph::createSelection(TIntermSelection* intermSelection)
{
TGraphSelection* selection = new TGraphSelection(intermSelection);
mAllNodes.push_back(selection);
return selection;
}
TGraphLoop* TDependencyGraph::createLoop(TIntermLoop* intermLoop)
{
TGraphLoop* loop = new TGraphLoop(intermLoop);
mAllNodes.push_back(loop);
return loop;
}
TGraphLogicalOp* TDependencyGraph::createLogicalOp(TIntermBinary* intermLogicalOp)
{
TGraphLogicalOp* logicalOp = new TGraphLogicalOp(intermLogicalOp);
mAllNodes.push_back(logicalOp);
return logicalOp;
}
const char* TGraphLogicalOp::getOpString() const
{
const char* opString = NULL;
switch (getIntermLogicalOp()->getOp()) {
case EOpLogicalAnd: opString = "and"; break;
case EOpLogicalOr: opString = "or"; break;
default: opString = "unknown"; break;
}
return opString;
}
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPH_H_
#define COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPH_H_
#include "compiler/translator/IntermNode.h"
#include <set>
#include <stack>
class TGraphNode;
class TGraphParentNode;
class TGraphArgument;
class TGraphFunctionCall;
class TGraphSymbol;
class TGraphSelection;
class TGraphLoop;
class TGraphLogicalOp;
class TDependencyGraphTraverser;
class TDependencyGraphOutput;
typedef std::set<TGraphNode*> TGraphNodeSet;
typedef std::vector<TGraphNode*> TGraphNodeVector;
typedef std::vector<TGraphSymbol*> TGraphSymbolVector;
typedef std::vector<TGraphFunctionCall*> TFunctionCallVector;
//
// Base class for all dependency graph nodes.
//
class TGraphNode {
public:
TGraphNode(TIntermNode* node) : intermNode(node) {}
virtual ~TGraphNode() {}
virtual void traverse(TDependencyGraphTraverser* graphTraverser);
protected:
TIntermNode* intermNode;
};
//
// Base class for dependency graph nodes that may have children.
//
class TGraphParentNode : public TGraphNode {
public:
TGraphParentNode(TIntermNode* node) : TGraphNode(node) {}
~TGraphParentNode() override {}
void addDependentNode(TGraphNode* node) { if (node != this) mDependentNodes.insert(node); }
void traverse(TDependencyGraphTraverser *graphTraverser) override;
private:
TGraphNodeSet mDependentNodes;
};
//
// Handle function call arguments.
//
class TGraphArgument : public TGraphParentNode {
public:
TGraphArgument(TIntermAggregate* intermFunctionCall, int argumentNumber)
: TGraphParentNode(intermFunctionCall)
, mArgumentNumber(argumentNumber) {}
~TGraphArgument() override {}
const TIntermAggregate* getIntermFunctionCall() const { return intermNode->getAsAggregate(); }
int getArgumentNumber() const { return mArgumentNumber; }
void traverse(TDependencyGraphTraverser *graphTraverser) override;
private:
int mArgumentNumber;
};
//
// Handle function calls.
//
class TGraphFunctionCall : public TGraphParentNode {
public:
TGraphFunctionCall(TIntermAggregate* intermFunctionCall)
: TGraphParentNode(intermFunctionCall) {}
~TGraphFunctionCall() override {}
const TIntermAggregate* getIntermFunctionCall() const { return intermNode->getAsAggregate(); }
void traverse(TDependencyGraphTraverser *graphTraverser) override;
};
//
// Handle symbols.
//
class TGraphSymbol : public TGraphParentNode {
public:
TGraphSymbol(TIntermSymbol* intermSymbol) : TGraphParentNode(intermSymbol) {}
~TGraphSymbol() override {}
const TIntermSymbol* getIntermSymbol() const { return intermNode->getAsSymbolNode(); }
void traverse(TDependencyGraphTraverser *graphTraverser) override;
};
//
// Handle if statements and ternary operators.
//
class TGraphSelection : public TGraphNode {
public:
TGraphSelection(TIntermSelection* intermSelection) : TGraphNode(intermSelection) {}
~TGraphSelection() override {}
const TIntermSelection* getIntermSelection() const { return intermNode->getAsSelectionNode(); }
void traverse(TDependencyGraphTraverser *graphTraverser) override;
};
//
// Handle for, do-while, and while loops.
//
class TGraphLoop : public TGraphNode {
public:
TGraphLoop(TIntermLoop* intermLoop) : TGraphNode(intermLoop) {}
~TGraphLoop() override {}
const TIntermLoop* getIntermLoop() const { return intermNode->getAsLoopNode(); }
void traverse(TDependencyGraphTraverser *graphTraverser) override;
};
//
// Handle logical and, or.
//
class TGraphLogicalOp : public TGraphNode {
public:
TGraphLogicalOp(TIntermBinary* intermLogicalOp) : TGraphNode(intermLogicalOp) {}
~TGraphLogicalOp() override {}
const TIntermBinary* getIntermLogicalOp() const { return intermNode->getAsBinaryNode(); }
const char* getOpString() const;
void traverse(TDependencyGraphTraverser *graphTraverser) override;
};
//
// A dependency graph of symbols, function calls, conditions etc.
//
// This class provides an interface to the entry points of the dependency graph.
//
// Dependency graph nodes should be created by using one of the provided "create..." methods.
// This class (and nobody else) manages the memory of the created nodes.
// Nodes may not be removed after being added, so all created nodes will exist while the
// TDependencyGraph instance exists.
//
class TDependencyGraph {
public:
TDependencyGraph(TIntermNode* intermNode);
~TDependencyGraph();
const TGraphNodeVector &allNodes() const { return mAllNodes; }
const TGraphSymbolVector &samplerSymbols() const { return mSamplerSymbols; }
const TFunctionCallVector &userDefinedFunctionCalls() const
{
return mUserDefinedFunctionCalls;
}
TGraphArgument* createArgument(TIntermAggregate* intermFunctionCall, int argumentNumber);
TGraphFunctionCall* createFunctionCall(TIntermAggregate* intermFunctionCall);
TGraphSymbol* getOrCreateSymbol(TIntermSymbol* intermSymbol);
TGraphSelection* createSelection(TIntermSelection* intermSelection);
TGraphLoop* createLoop(TIntermLoop* intermLoop);
TGraphLogicalOp* createLogicalOp(TIntermBinary* intermLogicalOp);
private:
typedef TMap<int, TGraphSymbol*> TSymbolIdMap;
typedef std::pair<int, TGraphSymbol*> TSymbolIdPair;
TGraphNodeVector mAllNodes;
TGraphSymbolVector mSamplerSymbols;
TFunctionCallVector mUserDefinedFunctionCalls;
TSymbolIdMap mSymbolIdMap;
};
//
// For traversing the dependency graph. Users should derive from this,
// put their traversal specific data in it, and then pass it to a
// traverse method.
//
// When using this, just fill in the methods for nodes you want visited.
//
class TDependencyGraphTraverser : angle::NonCopyable {
public:
TDependencyGraphTraverser() : mDepth(0) {}
virtual ~TDependencyGraphTraverser() {}
virtual void visitSymbol(TGraphSymbol* symbol) {};
virtual void visitArgument(TGraphArgument* selection) {};
virtual void visitFunctionCall(TGraphFunctionCall* functionCall) {};
virtual void visitSelection(TGraphSelection* selection) {};
virtual void visitLoop(TGraphLoop* loop) {};
virtual void visitLogicalOp(TGraphLogicalOp* logicalOp) {};
int getDepth() const { return mDepth; }
void incrementDepth() { ++mDepth; }
void decrementDepth() { --mDepth; }
void clearVisited() { mVisited.clear(); }
void markVisited(TGraphNode* node) { mVisited.insert(node); }
bool isVisited(TGraphNode* node) const { return mVisited.find(node) != mVisited.end(); }
private:
int mDepth;
TGraphNodeSet mVisited;
};
#endif // COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPH_H_
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/depgraph/DependencyGraphBuilder.h"
void TDependencyGraphBuilder::build(TIntermNode *node, TDependencyGraph *graph)
{
TDependencyGraphBuilder builder(graph);
builder.build(node);
}
bool TDependencyGraphBuilder::visitAggregate(
Visit visit, TIntermAggregate *intermAggregate)
{
switch (intermAggregate->getOp())
{
case EOpFunction:
visitFunctionDefinition(intermAggregate);
break;
case EOpFunctionCall:
visitFunctionCall(intermAggregate);
break;
default:
visitAggregateChildren(intermAggregate);
break;
}
return false;
}
void TDependencyGraphBuilder::visitFunctionDefinition(
TIntermAggregate *intermAggregate)
{
// Currently, we do not support user defined functions.
if (intermAggregate->getName() != "main(")
return;
visitAggregateChildren(intermAggregate);
}
// Takes an expression like "f(x)" and creates a dependency graph like
// "x -> argument 0 -> function call".
void TDependencyGraphBuilder::visitFunctionCall(
TIntermAggregate *intermFunctionCall)
{
TGraphFunctionCall *functionCall =
mGraph->createFunctionCall(intermFunctionCall);
// Run through the function call arguments.
int argumentNumber = 0;
TIntermSequence *intermArguments = intermFunctionCall->getSequence();
for (TIntermSequence::const_iterator iter = intermArguments->begin();
iter != intermArguments->end();
++iter, ++argumentNumber)
{
TNodeSetMaintainer nodeSetMaintainer(this);
TIntermNode *intermArgument = *iter;
intermArgument->traverse(this);
if (TParentNodeSet *argumentNodes = mNodeSets.getTopSet())
{
TGraphArgument *argument = mGraph->createArgument(
intermFunctionCall, argumentNumber);
connectMultipleNodesToSingleNode(argumentNodes, argument);
argument->addDependentNode(functionCall);
}
}
// Push the leftmost symbol of this function call into the current set of
// dependent symbols to represent the result of this function call.
// Thus, an expression like "y = f(x)" will yield a dependency graph like
// "x -> argument 0 -> function call -> y".
// This line essentially passes the function call node back up to an earlier
// visitAssignment call, which will create the connection "function call -> y".
mNodeSets.insertIntoTopSet(functionCall);
}
void TDependencyGraphBuilder::visitAggregateChildren(
TIntermAggregate *intermAggregate)
{
TIntermSequence *sequence = intermAggregate->getSequence();
for (TIntermSequence::const_iterator iter = sequence->begin();
iter != sequence->end(); ++iter)
{
TIntermNode *intermChild = *iter;
intermChild->traverse(this);
}
}
void TDependencyGraphBuilder::visitSymbol(TIntermSymbol *intermSymbol)
{
// Push this symbol into the set of dependent symbols for the current
// assignment or condition that we are traversing.
TGraphSymbol *symbol = mGraph->getOrCreateSymbol(intermSymbol);
mNodeSets.insertIntoTopSet(symbol);
// If this symbol is the current leftmost symbol under an assignment, replace
// the previous leftmost symbol with this symbol.
if (!mLeftmostSymbols.empty() && mLeftmostSymbols.top() != &mRightSubtree)
{
mLeftmostSymbols.pop();
mLeftmostSymbols.push(symbol);
}
}
bool TDependencyGraphBuilder::visitBinary(Visit visit, TIntermBinary *intermBinary)
{
TOperator op = intermBinary->getOp();
if (op == EOpInitialize || intermBinary->isAssignment())
visitAssignment(intermBinary);
else if (op == EOpLogicalAnd || op == EOpLogicalOr)
visitLogicalOp(intermBinary);
else
visitBinaryChildren(intermBinary);
return false;
}
void TDependencyGraphBuilder::visitAssignment(TIntermBinary *intermAssignment)
{
TIntermTyped *intermLeft = intermAssignment->getLeft();
if (!intermLeft)
return;
TGraphSymbol *leftmostSymbol = NULL;
{
TNodeSetMaintainer nodeSetMaintainer(this);
{
TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mLeftSubtree);
intermLeft->traverse(this);
leftmostSymbol = mLeftmostSymbols.top();
// After traversing the left subtree of this assignment, we should
// have found a real leftmost symbol, and the leftmost symbol should
// not be a placeholder.
ASSERT(leftmostSymbol != &mLeftSubtree);
ASSERT(leftmostSymbol != &mRightSubtree);
}
if (TIntermTyped *intermRight = intermAssignment->getRight())
{
TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mRightSubtree);
intermRight->traverse(this);
}
if (TParentNodeSet *assignmentNodes = mNodeSets.getTopSet())
connectMultipleNodesToSingleNode(assignmentNodes, leftmostSymbol);
}
// Push the leftmost symbol of this assignment into the current set of dependent
// symbols to represent the result of this assignment.
// An expression like "a = (b = c)" will yield a dependency graph like
// "c -> b -> a".
// This line essentially passes the leftmost symbol of the nested assignment
// ("b" in this example) back up to the earlier visitAssignment call for the
// outer assignment, which will create the connection "b -> a".
mNodeSets.insertIntoTopSet(leftmostSymbol);
}
void TDependencyGraphBuilder::visitLogicalOp(TIntermBinary *intermLogicalOp)
{
if (TIntermTyped *intermLeft = intermLogicalOp->getLeft())
{
TNodeSetPropagatingMaintainer nodeSetMaintainer(this);
intermLeft->traverse(this);
if (TParentNodeSet *leftNodes = mNodeSets.getTopSet())
{
TGraphLogicalOp *logicalOp = mGraph->createLogicalOp(intermLogicalOp);
connectMultipleNodesToSingleNode(leftNodes, logicalOp);
}
}
if (TIntermTyped *intermRight = intermLogicalOp->getRight())
{
TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mRightSubtree);
intermRight->traverse(this);
}
}
void TDependencyGraphBuilder::visitBinaryChildren(TIntermBinary *intermBinary)
{
if (TIntermTyped *intermLeft = intermBinary->getLeft())
intermLeft->traverse(this);
if (TIntermTyped *intermRight = intermBinary->getRight())
{
TLeftmostSymbolMaintainer leftmostSymbolMaintainer(this, mRightSubtree);
intermRight->traverse(this);
}
}
bool TDependencyGraphBuilder::visitSelection(
Visit visit, TIntermSelection *intermSelection)
{
if (TIntermNode *intermCondition = intermSelection->getCondition())
{
TNodeSetMaintainer nodeSetMaintainer(this);
intermCondition->traverse(this);
if (TParentNodeSet *conditionNodes = mNodeSets.getTopSet())
{
TGraphSelection *selection = mGraph->createSelection(intermSelection);
connectMultipleNodesToSingleNode(conditionNodes, selection);
}
}
if (TIntermNode *intermTrueBlock = intermSelection->getTrueBlock())
intermTrueBlock->traverse(this);
if (TIntermNode *intermFalseBlock = intermSelection->getFalseBlock())
intermFalseBlock->traverse(this);
return false;
}
bool TDependencyGraphBuilder::visitLoop(Visit visit, TIntermLoop *intermLoop)
{
if (TIntermTyped *intermCondition = intermLoop->getCondition())
{
TNodeSetMaintainer nodeSetMaintainer(this);
intermCondition->traverse(this);
if (TParentNodeSet *conditionNodes = mNodeSets.getTopSet())
{
TGraphLoop *loop = mGraph->createLoop(intermLoop);
connectMultipleNodesToSingleNode(conditionNodes, loop);
}
}
if (TIntermNode* intermBody = intermLoop->getBody())
intermBody->traverse(this);
if (TIntermTyped *intermExpression = intermLoop->getExpression())
intermExpression->traverse(this);
return false;
}
void TDependencyGraphBuilder::connectMultipleNodesToSingleNode(
TParentNodeSet *nodes, TGraphNode *node) const
{
for (TParentNodeSet::const_iterator iter = nodes->begin();
iter != nodes->end(); ++iter)
{
TGraphParentNode *currentNode = *iter;
currentNode->addDependentNode(node);
}
}
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHBUILDER_H_
#define COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHBUILDER_H_
#include "compiler/translator/depgraph/DependencyGraph.h"
//
// Creates a dependency graph of symbols, function calls, conditions etc. by
// traversing a intermediate tree.
//
class TDependencyGraphBuilder : public TIntermTraverser
{
public:
static void build(TIntermNode *node, TDependencyGraph *graph);
void visitSymbol(TIntermSymbol *) override;
bool visitBinary(Visit visit, TIntermBinary *) override;
bool visitSelection(Visit visit, TIntermSelection *) override;
bool visitAggregate(Visit visit, TIntermAggregate *) override;
bool visitLoop(Visit visit, TIntermLoop *) override;
private:
typedef std::stack<TGraphSymbol *> TSymbolStack;
typedef std::set<TGraphParentNode *> TParentNodeSet;
//
// For collecting the dependent nodes of assignments, conditions, etc.
// while traversing the intermediate tree.
//
// This data structure is stack of sets. Each set contains dependency graph
// parent nodes.
//
class TNodeSetStack
{
public:
TNodeSetStack() {};
~TNodeSetStack() { clear(); }
// This should only be called after a pushSet.
// Returns NULL if the top set is empty.
TParentNodeSet *getTopSet() const
{
ASSERT(!mNodeSets.empty());
TParentNodeSet *topSet = mNodeSets.top();
return !topSet->empty() ? topSet : NULL;
}
void pushSet() { mNodeSets.push(new TParentNodeSet()); }
void popSet()
{
ASSERT(!mNodeSets.empty());
delete mNodeSets.top();
mNodeSets.pop();
}
// Pops the top set and adds its contents to the new top set.
// This should only be called after a pushSet.
// If there is no set below the top set, the top set is just deleted.
void popSetIntoNext()
{
ASSERT(!mNodeSets.empty());
TParentNodeSet *oldTopSet = mNodeSets.top();
mNodeSets.pop();
if (!mNodeSets.empty())
{
TParentNodeSet *newTopSet = mNodeSets.top();
newTopSet->insert(oldTopSet->begin(), oldTopSet->end());
}
delete oldTopSet;
}
// Does nothing if there is no top set.
// This can be called when there is no top set if we are visiting
// symbols that are not under an assignment or condition.
// We don't need to track those symbols.
void insertIntoTopSet(TGraphParentNode *node)
{
if (mNodeSets.empty())
return;
mNodeSets.top()->insert(node);
}
void clear()
{
while (!mNodeSets.empty())
popSet();
}
private:
typedef std::stack<TParentNodeSet *> TParentNodeSetStack;
TParentNodeSetStack mNodeSets;
};
//
// An instance of this class pushes a new node set when instantiated.
// When the instance goes out of scope, it and pops the node set.
//
class TNodeSetMaintainer : angle::NonCopyable
{
public:
TNodeSetMaintainer(TDependencyGraphBuilder *factory)
: mSets(factory->mNodeSets)
{
mSets.pushSet();
}
~TNodeSetMaintainer() { mSets.popSet(); }
protected:
TNodeSetStack &mSets;
};
//
// An instance of this class pushes a new node set when instantiated.
// When the instance goes out of scope, it and pops the top node set and adds
// its contents to the new top node set.
//
class TNodeSetPropagatingMaintainer : angle::NonCopyable
{
public:
TNodeSetPropagatingMaintainer(TDependencyGraphBuilder *factory)
: mSets(factory->mNodeSets)
{
mSets.pushSet();
}
~TNodeSetPropagatingMaintainer() { mSets.popSetIntoNext(); }
protected:
TNodeSetStack &mSets;
};
//
// An instance of this class keeps track of the leftmost symbol while we're
// exploring an assignment.
// It will push the placeholder symbol kLeftSubtree when instantiated under a
// left subtree, and kRightSubtree under a right subtree.
// When it goes out of scope, it will pop the leftmost symbol at the top of the
// scope.
// During traversal, the TDependencyGraphBuilder will replace kLeftSubtree with
// a real symbol.
// kRightSubtree will never be replaced by a real symbol because we are tracking
// the leftmost symbol.
//
class TLeftmostSymbolMaintainer : angle::NonCopyable
{
public:
TLeftmostSymbolMaintainer(
TDependencyGraphBuilder *factory, TGraphSymbol &subtree)
: mLeftmostSymbols(factory->mLeftmostSymbols)
{
mNeedsPlaceholderSymbol =
mLeftmostSymbols.empty() || mLeftmostSymbols.top() != &subtree;
if (mNeedsPlaceholderSymbol)
mLeftmostSymbols.push(&subtree);
}
~TLeftmostSymbolMaintainer()
{
if (mNeedsPlaceholderSymbol)
mLeftmostSymbols.pop();
}
protected:
TSymbolStack& mLeftmostSymbols;
bool mNeedsPlaceholderSymbol;
};
TDependencyGraphBuilder(TDependencyGraph *graph)
: TIntermTraverser(true, false, false),
mLeftSubtree(NULL),
mRightSubtree(NULL),
mGraph(graph) {}
void build(TIntermNode *intermNode) { intermNode->traverse(this); }
void connectMultipleNodesToSingleNode(
TParentNodeSet *nodes, TGraphNode *node) const;
void visitAssignment(TIntermBinary *);
void visitLogicalOp(TIntermBinary *);
void visitBinaryChildren(TIntermBinary *);
void visitFunctionDefinition(TIntermAggregate *);
void visitFunctionCall(TIntermAggregate *intermFunctionCall);
void visitAggregateChildren(TIntermAggregate *);
TGraphSymbol mLeftSubtree;
TGraphSymbol mRightSubtree;
TDependencyGraph *mGraph;
TNodeSetStack mNodeSets;
TSymbolStack mLeftmostSymbols;
};
#endif // COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHBUILDER_H_
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/depgraph/DependencyGraphOutput.h"
void TDependencyGraphOutput::outputIndentation()
{
for (int i = 0; i < getDepth(); ++i)
mSink << " ";
}
void TDependencyGraphOutput::visitArgument(TGraphArgument* parameter)
{
outputIndentation();
mSink << "argument " << parameter->getArgumentNumber() << " of call to "
<< parameter->getIntermFunctionCall()->getName() << "\n";
}
void TDependencyGraphOutput::visitFunctionCall(TGraphFunctionCall* functionCall)
{
outputIndentation();
mSink << "function call " << functionCall->getIntermFunctionCall()->getName() << "\n";
}
void TDependencyGraphOutput::visitSymbol(TGraphSymbol* symbol)
{
outputIndentation();
mSink << symbol->getIntermSymbol()->getSymbol() << " (symbol id: "
<< symbol->getIntermSymbol()->getId() << ")\n";
}
void TDependencyGraphOutput::visitSelection(TGraphSelection* selection)
{
outputIndentation();
mSink << "selection\n";
}
void TDependencyGraphOutput::visitLoop(TGraphLoop* loop)
{
outputIndentation();
mSink << "loop condition\n";
}
void TDependencyGraphOutput::visitLogicalOp(TGraphLogicalOp* logicalOp)
{
outputIndentation();
mSink << "logical " << logicalOp->getOpString() << "\n";
}
void TDependencyGraphOutput::outputAllSpanningTrees(TDependencyGraph& graph)
{
mSink << "\n";
for (auto symbol : graph.allNodes())
{
mSink << "--- Dependency graph spanning tree ---\n";
clearVisited();
symbol->traverse(this);
mSink << "\n";
}
}
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHOUTPUT_H_
#define COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHOUTPUT_H_
#include "compiler/translator/depgraph/DependencyGraph.h"
#include "compiler/translator/InfoSink.h"
class TDependencyGraphOutput : public TDependencyGraphTraverser
{
public:
TDependencyGraphOutput(TInfoSinkBase& sink) : mSink(sink) {}
void visitSymbol(TGraphSymbol* symbol) override;
void visitArgument(TGraphArgument* parameter) override;
void visitFunctionCall(TGraphFunctionCall* functionCall) override;
void visitSelection(TGraphSelection* selection) override;
void visitLoop(TGraphLoop* loop) override;
void visitLogicalOp(TGraphLogicalOp* logicalOp) override;
void outputAllSpanningTrees(TDependencyGraph& graph);
private:
void outputIndentation();
TInfoSinkBase& mSink;
};
#endif // COMPILER_TRANSLATOR_DEPGRAPH_DEPENDENCYGRAPHOUTPUT_H_
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/depgraph/DependencyGraph.h"
// These methods do a breadth-first traversal through the graph and mark visited nodes.
void TGraphNode::traverse(TDependencyGraphTraverser* graphTraverser)
{
graphTraverser->markVisited(this);
}
void TGraphParentNode::traverse(TDependencyGraphTraverser* graphTraverser)
{
TGraphNode::traverse(graphTraverser);
graphTraverser->incrementDepth();
// Visit the parent node's children.
for (TGraphNodeSet::const_iterator iter = mDependentNodes.begin();
iter != mDependentNodes.end();
++iter)
{
TGraphNode* node = *iter;
if (!graphTraverser->isVisited(node))
node->traverse(graphTraverser);
}
graphTraverser->decrementDepth();
}
void TGraphArgument::traverse(TDependencyGraphTraverser* graphTraverser)
{
graphTraverser->visitArgument(this);
TGraphParentNode::traverse(graphTraverser);
}
void TGraphFunctionCall::traverse(TDependencyGraphTraverser* graphTraverser)
{
graphTraverser->visitFunctionCall(this);
TGraphParentNode::traverse(graphTraverser);
}
void TGraphSymbol::traverse(TDependencyGraphTraverser* graphTraverser)
{
graphTraverser->visitSymbol(this);
TGraphParentNode::traverse(graphTraverser);
}
void TGraphSelection::traverse(TDependencyGraphTraverser* graphTraverser)
{
graphTraverser->visitSelection(this);
TGraphNode::traverse(graphTraverser);
}
void TGraphLoop::traverse(TDependencyGraphTraverser* graphTraverser)
{
graphTraverser->visitLoop(this);
TGraphNode::traverse(graphTraverser);
}
void TGraphLogicalOp::traverse(TDependencyGraphTraverser* graphTraverser)
{
graphTraverser->visitLogicalOp(this);
TGraphNode::traverse(graphTraverser);
}
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/ParseContext.h"
#include "compiler/translator/depgraph/DependencyGraphOutput.h"
#include "compiler/translator/timing/RestrictFragmentShaderTiming.h"
RestrictFragmentShaderTiming::RestrictFragmentShaderTiming(TInfoSinkBase& sink)
: mSink(sink)
, mNumErrors(0)
{
// Sampling ops found only in fragment shaders.
mSamplingOps.insert("texture2D(s21;vf2;f1;");
mSamplingOps.insert("texture2DProj(s21;vf3;f1;");
mSamplingOps.insert("texture2DProj(s21;vf4;f1;");
mSamplingOps.insert("textureCube(sC1;vf3;f1;");
// Sampling ops found in both vertex and fragment shaders.
mSamplingOps.insert("texture2D(s21;vf2;");
mSamplingOps.insert("texture2DProj(s21;vf3;");
mSamplingOps.insert("texture2DProj(s21;vf4;");
mSamplingOps.insert("textureCube(sC1;vf3;");
// Sampling ops provided by OES_EGL_image_external.
mSamplingOps.insert("texture2D(1;vf2;");
mSamplingOps.insert("texture2DProj(1;vf3;");
mSamplingOps.insert("texture2DProj(1;vf4;");
// Sampling ops provided by ARB_texture_rectangle.
mSamplingOps.insert("texture2DRect(1;vf2;");
mSamplingOps.insert("texture2DRectProj(1;vf3;");
mSamplingOps.insert("texture2DRectProj(1;vf4;");
// Sampling ops provided by EXT_shader_texture_lod.
mSamplingOps.insert("texture2DLodEXT(1;vf2;f1;");
mSamplingOps.insert("texture2DProjLodEXT(1;vf3;f1;");
mSamplingOps.insert("texture2DProjLodEXT(1;vf4;f1;");
mSamplingOps.insert("textureCubeLodEXT(1;vf4;f1;");
mSamplingOps.insert("texture2DGradEXT(1;vf2;vf2;vf2;");
mSamplingOps.insert("texture2DProjGradEXT(1;vf3;vf2;vf2;");
mSamplingOps.insert("texture2DProjGradEXT(1;vf4;vf2;vf2;");
mSamplingOps.insert("textureCubeGradEXT(1;vf3;vf3;vf3;");
}
// FIXME(mvujovic): We do not know if the execution time of built-in operations like sin, pow, etc.
// can vary based on the value of the input arguments. If so, we should restrict those as well.
void RestrictFragmentShaderTiming::enforceRestrictions(const TDependencyGraph& graph)
{
mNumErrors = 0;
// FIXME(mvujovic): The dependency graph does not support user defined function calls right now,
// so we generate errors for them.
validateUserDefinedFunctionCallUsage(graph);
// Starting from each sampler, traverse the dependency graph and generate an error each time we
// hit a node where sampler dependent values are not allowed.
for (auto samplerSymbol : graph.samplerSymbols())
{
clearVisited();
samplerSymbol->traverse(this);
}
}
void RestrictFragmentShaderTiming::validateUserDefinedFunctionCallUsage(const TDependencyGraph& graph)
{
for (const auto* functionCall : graph.userDefinedFunctionCalls())
{
beginError(functionCall->getIntermFunctionCall());
mSink << "A call to a user defined function is not permitted.\n";
}
}
void RestrictFragmentShaderTiming::beginError(const TIntermNode* node)
{
++mNumErrors;
mSink.prefix(EPrefixError);
mSink.location(node->getLine());
}
bool RestrictFragmentShaderTiming::isSamplingOp(const TIntermAggregate* intermFunctionCall) const
{
return !intermFunctionCall->isUserDefined() &&
mSamplingOps.find(intermFunctionCall->getName()) != mSamplingOps.end();
}
void RestrictFragmentShaderTiming::visitArgument(TGraphArgument* parameter)
{
// Texture cache access time might leak sensitive information.
// Thus, we restrict sampler dependent values from affecting the coordinate or LOD bias of a
// sampling operation.
if (isSamplingOp(parameter->getIntermFunctionCall())) {
switch (parameter->getArgumentNumber()) {
case 1:
// Second argument (coord)
beginError(parameter->getIntermFunctionCall());
mSink << "An expression dependent on a sampler is not permitted to be the"
<< " coordinate argument of a sampling operation.\n";
break;
case 2:
// Third argument (bias)
beginError(parameter->getIntermFunctionCall());
mSink << "An expression dependent on a sampler is not permitted to be the"
<< " bias argument of a sampling operation.\n";
break;
default:
// First argument (sampler)
break;
}
}
}
void RestrictFragmentShaderTiming::visitSelection(TGraphSelection* selection)
{
beginError(selection->getIntermSelection());
mSink << "An expression dependent on a sampler is not permitted in a conditional statement.\n";
}
void RestrictFragmentShaderTiming::visitLoop(TGraphLoop* loop)
{
beginError(loop->getIntermLoop());
mSink << "An expression dependent on a sampler is not permitted in a loop condition.\n";
}
void RestrictFragmentShaderTiming::visitLogicalOp(TGraphLogicalOp* logicalOp)
{
beginError(logicalOp->getIntermLogicalOp());
mSink << "An expression dependent on a sampler is not permitted on the left hand side of a logical "
<< logicalOp->getOpString()
<< " operator.\n";
}
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILER_TRANSLATOR_TIMING_RESTRICTFRAGMENTSHADERTIMING_H_
#define COMPILER_TRANSLATOR_TIMING_RESTRICTFRAGMENTSHADERTIMING_H_
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/depgraph/DependencyGraph.h"
class TInfoSinkBase;
class RestrictFragmentShaderTiming : TDependencyGraphTraverser
{
public:
RestrictFragmentShaderTiming(TInfoSinkBase &sink);
void enforceRestrictions(const TDependencyGraph &graph);
int numErrors() const { return mNumErrors; }
void visitArgument(TGraphArgument *parameter) override;
void visitSelection(TGraphSelection *selection) override;
void visitLoop(TGraphLoop *loop) override;
void visitLogicalOp(TGraphLogicalOp *logicalOp) override;
private:
void beginError(const TIntermNode *node);
void validateUserDefinedFunctionCallUsage(const TDependencyGraph &graph);
bool isSamplingOp(const TIntermAggregate *intermFunctionCall) const;
TInfoSinkBase &mSink;
int mNumErrors;
typedef std::set<TString> StringSet;
StringSet mSamplingOps;
};
#endif // COMPILER_TRANSLATOR_TIMING_RESTRICTFRAGMENTSHADERTIMING_H_
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/timing/RestrictVertexShaderTiming.h"
void RestrictVertexShaderTiming::visitSymbol(TIntermSymbol* node)
{
if (IsSampler(node->getBasicType())) {
++mNumErrors;
mSink.message(EPrefixError,
node->getLine(),
"Samplers are not permitted in vertex shaders.\n");
}
}
//
// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#ifndef COMPILER_TRANSLATOR_TIMING_RESTRICTVERTEXSHADERTIMING_H_
#define COMPILER_TRANSLATOR_TIMING_RESTRICTVERTEXSHADERTIMING_H_
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/InfoSink.h"
class TInfoSinkBase;
class RestrictVertexShaderTiming : public TIntermTraverser {
public:
RestrictVertexShaderTiming(TInfoSinkBase& sink)
: TIntermTraverser(true, false, false)
, mSink(sink)
, mNumErrors(0) {}
void enforceRestrictions(TIntermNode* root) { root->traverse(this); }
int numErrors() { return mNumErrors; }
void visitSymbol(TIntermSymbol *) override;
private:
TInfoSinkBase& mSink;
int mNumErrors;
};
#endif // COMPILER_TRANSLATOR_TIMING_RESTRICTVERTEXSHADERTIMING_H_
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