зеркало из https://github.com/AvaloniaUI/angle.git
Improvement on loop unrolling with loops indexing sampler arrays
1) Before this workaround is hardwired on mac, now we move it behind a compil 2) Fix the issue where "break" inside the loop isn't handled while unrolled. BUG=338474 TEST=webgl conformance test sampler-array-using-loop-index.html Change-Id: I4996a42c2dea39a8a5af772c256f8e3cb383f59a Reviewed-on: https://chromium-review.googlesource.com/188079 Reviewed-by: Zhenyao Mo <zmo@chromium.org> Tested-by: Zhenyao Mo <zmo@chromium.org> Conflicts: include/GLSLANG/ShaderLang.h src/compiler/translator/ValidateLimitations.cpp Change-Id: I546197bd7df1634ebccdd380be14c3250cd56151 Reviewed-on: https://chromium-review.googlesource.com/189061 Reviewed-by: Shannon Woods <shannonwoods@chromium.org> Tested-by: Zhenyao Mo <zmo@chromium.org>
This commit is contained in:
Родитель
2515c5201b
Коммит
550c600b99
|
@ -170,9 +170,14 @@ typedef enum {
|
|||
SH_SOURCE_PATH = 0x0020,
|
||||
SH_MAP_LONG_VARIABLE_NAMES = 0x0040,
|
||||
SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = 0x0080,
|
||||
// If a sampler array index happens to be a loop index,
|
||||
// 1) if its type is integer, unroll the loop.
|
||||
// 2) if its type is float, fail the shader compile.
|
||||
// This is to work around a mac driver bug.
|
||||
SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX = 0x0100,
|
||||
|
||||
// This is needed only as a workaround for certain OpenGL driver bugs.
|
||||
SH_EMULATE_BUILT_IN_FUNCTIONS = 0x0100,
|
||||
SH_EMULATE_BUILT_IN_FUNCTIONS = 0x0200,
|
||||
|
||||
// This is an experimental flag to enforce restrictions that aim to prevent
|
||||
// timing attacks.
|
||||
|
@ -180,15 +185,15 @@ typedef enum {
|
|||
// 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,
|
||||
|
||||
SH_TIMING_RESTRICTIONS = 0x0400,
|
||||
|
||||
// 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 SH_FRAGMENT_SHADER.
|
||||
SH_DEPENDENCY_GRAPH = 0x0400,
|
||||
SH_DEPENDENCY_GRAPH = 0x0800,
|
||||
|
||||
// Enforce the GLSL 1.017 Appendix A section 7 packing restrictions.
|
||||
// This flag only enforces (and can only enforce) the packing
|
||||
|
@ -196,7 +201,7 @@ typedef enum {
|
|||
// shaders. ShCheckVariablesWithinPackingLimits() lets embedders
|
||||
// enforce the packing restrictions for varying variables during
|
||||
// program link time.
|
||||
SH_ENFORCE_PACKING_RESTRICTIONS = 0x0800,
|
||||
SH_ENFORCE_PACKING_RESTRICTIONS = 0x1000,
|
||||
|
||||
// This flag ensures all indirect (expression-based) array indexing
|
||||
// is clamped to the bounds of the array. This ensures, for example,
|
||||
|
@ -204,32 +209,32 @@ typedef enum {
|
|||
// vec234, or mat234 type. The ShArrayIndexClampingStrategy enum,
|
||||
// specified in the ShBuiltInResources when constructing the
|
||||
// compiler, selects the strategy for the clamping implementation.
|
||||
SH_CLAMP_INDIRECT_ARRAY_BOUNDS = 0x1000,
|
||||
SH_CLAMP_INDIRECT_ARRAY_BOUNDS = 0x2000,
|
||||
|
||||
// This flag limits the complexity of an expression.
|
||||
SH_LIMIT_EXPRESSION_COMPLEXITY = 0x2000,
|
||||
SH_LIMIT_EXPRESSION_COMPLEXITY = 0x4000,
|
||||
|
||||
// This flag limits the depth of the call stack.
|
||||
SH_LIMIT_CALL_STACK_DEPTH = 0x4000,
|
||||
SH_LIMIT_CALL_STACK_DEPTH = 0x8000,
|
||||
|
||||
// This flag initializes gl_Position to vec4(0,0,0,0) at the
|
||||
// beginning of the vertex shader's main(), and has no effect in the
|
||||
// fragment shader. It is intended as a workaround for drivers which
|
||||
// incorrectly fail to link programs if gl_Position is not written.
|
||||
SH_INIT_GL_POSITION = 0x8000,
|
||||
SH_INIT_GL_POSITION = 0x10000,
|
||||
|
||||
// This flag replaces
|
||||
// "a && b" with "a ? b : false",
|
||||
// "a || b" with "a ? true : b".
|
||||
// This is to work around a MacOSX driver bug that |b| is executed
|
||||
// independent of |a|'s value.
|
||||
SH_UNFOLD_SHORT_CIRCUIT = 0x10000,
|
||||
SH_UNFOLD_SHORT_CIRCUIT = 0x20000,
|
||||
|
||||
// This flag initializes varyings without static use in vertex shader
|
||||
// at the beginning of main(), and has no effects in the fragment shader.
|
||||
// It is intended as a workaround for drivers which incorrectly optimize
|
||||
// out such varyings and cause a link failure.
|
||||
SH_INIT_VARYINGS_WITHOUT_STATIC_USE = 0x20000,
|
||||
SH_INIT_VARYINGS_WITHOUT_STATIC_USE = 0x40000,
|
||||
} ShCompileOptions;
|
||||
|
||||
// Defines alternate strategies for implementing array index clamping.
|
||||
|
|
|
@ -201,7 +201,21 @@ bool TCompiler::compile(const char* const shaderStrings[],
|
|||
|
||||
// Unroll for-loop markup needs to happen after validateLimitations pass.
|
||||
if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
|
||||
ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(root);
|
||||
{
|
||||
ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex);
|
||||
root->traverse(&marker);
|
||||
}
|
||||
if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX))
|
||||
{
|
||||
ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex);
|
||||
root->traverse(&marker);
|
||||
if (marker.samplerArrayIndexIsFloatLoopIndex())
|
||||
{
|
||||
infoSink.info.prefix(EPrefixError);
|
||||
infoSink.info << "sampler array index is float loop index";
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Built-in function emulation needs to happen after validateLimitations pass.
|
||||
if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
|
||||
|
|
|
@ -6,210 +6,77 @@
|
|||
|
||||
#include "compiler/translator/ForLoopUnroll.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class IntegerForLoopUnrollMarker : public TIntermTraverser {
|
||||
public:
|
||||
|
||||
virtual bool visitLoop(Visit, TIntermLoop* node)
|
||||
{
|
||||
// This is called after ValidateLimitations pass, so all the ASSERT
|
||||
// should never fail.
|
||||
// See ValidateLimitations::validateForLoopInit().
|
||||
ASSERT(node);
|
||||
ASSERT(node->getType() == ELoopFor);
|
||||
ASSERT(node->getInit());
|
||||
TIntermAggregate* decl = node->getInit()->getAsAggregate();
|
||||
ASSERT(decl && decl->getOp() == EOpDeclaration);
|
||||
TIntermSequence& declSeq = decl->getSequence();
|
||||
ASSERT(declSeq.size() == 1);
|
||||
TIntermBinary* declInit = declSeq[0]->getAsBinaryNode();
|
||||
ASSERT(declInit && declInit->getOp() == EOpInitialize);
|
||||
ASSERT(declInit->getLeft());
|
||||
TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode();
|
||||
ASSERT(symbol);
|
||||
TBasicType type = symbol->getBasicType();
|
||||
ASSERT(type == EbtInt || type == EbtFloat);
|
||||
if (type == EbtInt)
|
||||
node->setUnrollFlag(true);
|
||||
bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node)
|
||||
{
|
||||
if (mUnrollCondition != kSamplerArrayIndex)
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // anonymous namepsace
|
||||
|
||||
void ForLoopUnroll::FillLoopIndexInfo(TIntermLoop* node, TLoopIndexInfo& info)
|
||||
{
|
||||
ASSERT(node->getType() == ELoopFor);
|
||||
ASSERT(node->getUnrollFlag());
|
||||
|
||||
TIntermNode* init = node->getInit();
|
||||
ASSERT(init != NULL);
|
||||
TIntermAggregate* decl = init->getAsAggregate();
|
||||
ASSERT((decl != NULL) && (decl->getOp() == EOpDeclaration));
|
||||
TIntermSequence& declSeq = decl->getSequence();
|
||||
ASSERT(declSeq.size() == 1);
|
||||
TIntermBinary* declInit = declSeq[0]->getAsBinaryNode();
|
||||
ASSERT((declInit != NULL) && (declInit->getOp() == EOpInitialize));
|
||||
TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode();
|
||||
ASSERT(symbol != NULL);
|
||||
ASSERT(symbol->getBasicType() == EbtInt);
|
||||
|
||||
info.id = symbol->getId();
|
||||
|
||||
ASSERT(declInit->getRight() != NULL);
|
||||
TIntermConstantUnion* initNode = declInit->getRight()->getAsConstantUnion();
|
||||
ASSERT(initNode != NULL);
|
||||
|
||||
info.initValue = evaluateIntConstant(initNode);
|
||||
info.currentValue = info.initValue;
|
||||
|
||||
TIntermNode* cond = node->getCondition();
|
||||
ASSERT(cond != NULL);
|
||||
TIntermBinary* binOp = cond->getAsBinaryNode();
|
||||
ASSERT(binOp != NULL);
|
||||
ASSERT(binOp->getRight() != NULL);
|
||||
ASSERT(binOp->getRight()->getAsConstantUnion() != NULL);
|
||||
|
||||
info.incrementValue = getLoopIncrement(node);
|
||||
info.stopValue = evaluateIntConstant(
|
||||
binOp->getRight()->getAsConstantUnion());
|
||||
info.op = binOp->getOp();
|
||||
}
|
||||
|
||||
void ForLoopUnroll::Step()
|
||||
{
|
||||
ASSERT(mLoopIndexStack.size() > 0);
|
||||
TLoopIndexInfo& info = mLoopIndexStack[mLoopIndexStack.size() - 1];
|
||||
info.currentValue += info.incrementValue;
|
||||
}
|
||||
|
||||
bool ForLoopUnroll::SatisfiesLoopCondition()
|
||||
{
|
||||
ASSERT(mLoopIndexStack.size() > 0);
|
||||
TLoopIndexInfo& info = mLoopIndexStack[mLoopIndexStack.size() - 1];
|
||||
// Relational operator is one of: > >= < <= == or !=.
|
||||
switch (info.op) {
|
||||
case EOpEqual:
|
||||
return (info.currentValue == info.stopValue);
|
||||
case EOpNotEqual:
|
||||
return (info.currentValue != info.stopValue);
|
||||
case EOpLessThan:
|
||||
return (info.currentValue < info.stopValue);
|
||||
case EOpGreaterThan:
|
||||
return (info.currentValue > info.stopValue);
|
||||
case EOpLessThanEqual:
|
||||
return (info.currentValue <= info.stopValue);
|
||||
case EOpGreaterThanEqual:
|
||||
return (info.currentValue >= info.stopValue);
|
||||
// If a sampler array index is also the loop index,
|
||||
// 1) if the index type is integer, mark the loop for unrolling;
|
||||
// 2) if the index type if float, set a flag to later fail compile.
|
||||
switch (node->getOp())
|
||||
{
|
||||
case EOpIndexIndirect:
|
||||
if (node->getLeft() != NULL && node->getRight() != NULL && node->getLeft()->getAsSymbolNode())
|
||||
{
|
||||
TIntermSymbol *symbol = node->getLeft()->getAsSymbolNode();
|
||||
if (IsSampler(symbol->getBasicType()) && symbol->isArray() && !mLoopStack.empty())
|
||||
{
|
||||
mVisitSamplerArrayIndexNodeInsideLoop = true;
|
||||
node->getRight()->traverse(this);
|
||||
mVisitSamplerArrayIndexNodeInsideLoop = false;
|
||||
// We have already visited all the children.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node)
|
||||
{
|
||||
if (mUnrollCondition == kIntegerIndex)
|
||||
{
|
||||
// Check if loop index type is integer.
|
||||
// This is called after ValidateLimitations pass, so all the calls
|
||||
// should be valid. See ValidateLimitations::validateForLoopInit().
|
||||
TIntermSequence& declSeq = node->getInit()->getAsAggregate()->getSequence();
|
||||
TIntermSymbol* symbol = declSeq[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
|
||||
if (symbol->getBasicType() == EbtInt)
|
||||
node->setUnrollFlag(true);
|
||||
}
|
||||
|
||||
TIntermNode *body = node->getBody();
|
||||
if (body != NULL)
|
||||
{
|
||||
mLoopStack.push(node);
|
||||
body->traverse(this);
|
||||
mLoopStack.pop();
|
||||
}
|
||||
// The loop is fully processed - no need to visit children.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ForLoopUnroll::NeedsToReplaceSymbolWithValue(TIntermSymbol* symbol)
|
||||
void ForLoopUnrollMarker::visitSymbol(TIntermSymbol* symbol)
|
||||
{
|
||||
for (TVector<TLoopIndexInfo>::iterator i = mLoopIndexStack.begin();
|
||||
i != mLoopIndexStack.end();
|
||||
++i) {
|
||||
if (i->id == symbol->getId())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int ForLoopUnroll::GetLoopIndexValue(TIntermSymbol* symbol)
|
||||
{
|
||||
for (TVector<TLoopIndexInfo>::iterator i = mLoopIndexStack.begin();
|
||||
i != mLoopIndexStack.end();
|
||||
++i) {
|
||||
if (i->id == symbol->getId())
|
||||
return i->currentValue;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
|
||||
void ForLoopUnroll::Push(TLoopIndexInfo& info)
|
||||
{
|
||||
mLoopIndexStack.push_back(info);
|
||||
}
|
||||
|
||||
void ForLoopUnroll::Pop()
|
||||
{
|
||||
mLoopIndexStack.pop_back();
|
||||
}
|
||||
|
||||
// static
|
||||
void ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling(
|
||||
TIntermNode* root)
|
||||
{
|
||||
ASSERT(root);
|
||||
|
||||
IntegerForLoopUnrollMarker marker;
|
||||
root->traverse(&marker);
|
||||
}
|
||||
|
||||
int ForLoopUnroll::getLoopIncrement(TIntermLoop* node)
|
||||
{
|
||||
TIntermNode* expr = node->getExpression();
|
||||
ASSERT(expr != NULL);
|
||||
// for expression has one of the following forms:
|
||||
// loop_index++
|
||||
// loop_index--
|
||||
// loop_index += constant_expression
|
||||
// loop_index -= constant_expression
|
||||
// ++loop_index
|
||||
// --loop_index
|
||||
// The last two forms are not specified in the spec, but I am assuming
|
||||
// its an oversight.
|
||||
TIntermUnary* unOp = expr->getAsUnaryNode();
|
||||
TIntermBinary* binOp = unOp ? NULL : expr->getAsBinaryNode();
|
||||
|
||||
TOperator op = EOpNull;
|
||||
TIntermConstantUnion* incrementNode = NULL;
|
||||
if (unOp != NULL) {
|
||||
op = unOp->getOp();
|
||||
} else if (binOp != NULL) {
|
||||
op = binOp->getOp();
|
||||
ASSERT(binOp->getRight() != NULL);
|
||||
incrementNode = binOp->getRight()->getAsConstantUnion();
|
||||
ASSERT(incrementNode != NULL);
|
||||
}
|
||||
|
||||
int increment = 0;
|
||||
// The operator is one of: ++ -- += -=.
|
||||
switch (op) {
|
||||
case EOpPostIncrement:
|
||||
case EOpPreIncrement:
|
||||
ASSERT((unOp != NULL) && (binOp == NULL));
|
||||
increment = 1;
|
||||
if (!mVisitSamplerArrayIndexNodeInsideLoop)
|
||||
return;
|
||||
TIntermLoop *loop = mLoopStack.findLoop(symbol);
|
||||
if (loop)
|
||||
{
|
||||
switch (symbol->getBasicType())
|
||||
{
|
||||
case EbtFloat:
|
||||
mSamplerArrayIndexIsFloatLoopIndex = true;
|
||||
break;
|
||||
case EOpPostDecrement:
|
||||
case EOpPreDecrement:
|
||||
ASSERT((unOp != NULL) && (binOp == NULL));
|
||||
increment = -1;
|
||||
case EbtInt:
|
||||
loop->setUnrollFlag(true);
|
||||
break;
|
||||
case EOpAddAssign:
|
||||
ASSERT((unOp == NULL) && (binOp != NULL));
|
||||
increment = evaluateIntConstant(incrementNode);
|
||||
break;
|
||||
case EOpSubAssign:
|
||||
ASSERT((unOp == NULL) && (binOp != NULL));
|
||||
increment = - evaluateIntConstant(incrementNode);
|
||||
break;
|
||||
default:
|
||||
ASSERT(false);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
return increment;
|
||||
}
|
||||
|
||||
int ForLoopUnroll::evaluateIntConstant(TIntermConstantUnion* node)
|
||||
{
|
||||
ASSERT((node != NULL) && (node->getUnionArrayPointer() != NULL));
|
||||
return node->getIConst(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,46 +7,44 @@
|
|||
#ifndef COMPILER_FORLOOPUNROLL_H_
|
||||
#define COMPILER_FORLOOPUNROLL_H_
|
||||
|
||||
#include "compiler/translator/intermediate.h"
|
||||
#include "compiler/translator/LoopInfo.h"
|
||||
|
||||
struct TLoopIndexInfo {
|
||||
int id;
|
||||
int initValue;
|
||||
int stopValue;
|
||||
int incrementValue;
|
||||
TOperator op;
|
||||
int currentValue;
|
||||
};
|
||||
// This class detects for-loops that needs to be unrolled.
|
||||
// Currently we support two unroll conditions:
|
||||
// 1) kForLoopWithIntegerIndex: unroll if the index type is integer.
|
||||
// 2) kForLoopWithSamplerArrayIndex: unroll where a sampler array index
|
||||
// is also the loop integer index, and reject and fail a compile
|
||||
// where a sampler array index is also the loop float index.
|
||||
class ForLoopUnrollMarker : public TIntermTraverser
|
||||
{
|
||||
public:
|
||||
enum UnrollCondition
|
||||
{
|
||||
kIntegerIndex,
|
||||
kSamplerArrayIndex
|
||||
};
|
||||
|
||||
class ForLoopUnroll {
|
||||
public:
|
||||
ForLoopUnroll() { }
|
||||
ForLoopUnrollMarker(UnrollCondition condition)
|
||||
: mUnrollCondition(condition),
|
||||
mSamplerArrayIndexIsFloatLoopIndex(false),
|
||||
mVisitSamplerArrayIndexNodeInsideLoop(false)
|
||||
{
|
||||
}
|
||||
|
||||
void FillLoopIndexInfo(TIntermLoop* node, TLoopIndexInfo& info);
|
||||
virtual bool visitBinary(Visit, TIntermBinary *node);
|
||||
virtual bool visitLoop(Visit, TIntermLoop *node);
|
||||
virtual void visitSymbol(TIntermSymbol *node);
|
||||
|
||||
// Update the info.currentValue for the next loop iteration.
|
||||
void Step();
|
||||
bool samplerArrayIndexIsFloatLoopIndex() const
|
||||
{
|
||||
return mSamplerArrayIndexIsFloatLoopIndex;
|
||||
}
|
||||
|
||||
// Return false if loop condition is no longer satisfied.
|
||||
bool SatisfiesLoopCondition();
|
||||
|
||||
// Check if the symbol is the index of a loop that's unrolled.
|
||||
bool NeedsToReplaceSymbolWithValue(TIntermSymbol* symbol);
|
||||
|
||||
// Return the current value of a given loop index symbol.
|
||||
int GetLoopIndexValue(TIntermSymbol* symbol);
|
||||
|
||||
void Push(TLoopIndexInfo& info);
|
||||
void Pop();
|
||||
|
||||
static void MarkForLoopsWithIntegerIndicesForUnrolling(TIntermNode* root);
|
||||
|
||||
private:
|
||||
int getLoopIncrement(TIntermLoop* node);
|
||||
|
||||
int evaluateIntConstant(TIntermConstantUnion* node);
|
||||
|
||||
TVector<TLoopIndexInfo> mLoopIndexStack;
|
||||
private:
|
||||
UnrollCondition mUnrollCondition;
|
||||
TLoopStack mLoopStack;
|
||||
bool mSamplerArrayIndexIsFloatLoopIndex;
|
||||
bool mVisitSamplerArrayIndexNodeInsideLoop;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
//
|
||||
// Copyright (c) 2002-2014 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/LoopInfo.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
int EvaluateIntConstant(TIntermConstantUnion *node)
|
||||
{
|
||||
ASSERT(node && node->getUnionArrayPointer());
|
||||
return node->getIConst(0);
|
||||
}
|
||||
|
||||
int GetLoopIntIncrement(TIntermLoop *node)
|
||||
{
|
||||
TIntermNode *expr = node->getExpression();
|
||||
// for expression has one of the following forms:
|
||||
// loop_index++
|
||||
// loop_index--
|
||||
// loop_index += constant_expression
|
||||
// loop_index -= constant_expression
|
||||
// ++loop_index
|
||||
// --loop_index
|
||||
// The last two forms are not specified in the spec, but I am assuming
|
||||
// its an oversight.
|
||||
TIntermUnary *unOp = expr->getAsUnaryNode();
|
||||
TIntermBinary *binOp = unOp ? NULL : expr->getAsBinaryNode();
|
||||
|
||||
TOperator op = EOpNull;
|
||||
TIntermConstantUnion *incrementNode = NULL;
|
||||
if (unOp)
|
||||
{
|
||||
op = unOp->getOp();
|
||||
}
|
||||
else if (binOp)
|
||||
{
|
||||
op = binOp->getOp();
|
||||
ASSERT(binOp->getRight());
|
||||
incrementNode = binOp->getRight()->getAsConstantUnion();
|
||||
ASSERT(incrementNode);
|
||||
}
|
||||
|
||||
int increment = 0;
|
||||
// The operator is one of: ++ -- += -=.
|
||||
switch (op)
|
||||
{
|
||||
case EOpPostIncrement:
|
||||
case EOpPreIncrement:
|
||||
ASSERT(unOp && !binOp);
|
||||
increment = 1;
|
||||
break;
|
||||
case EOpPostDecrement:
|
||||
case EOpPreDecrement:
|
||||
ASSERT(unOp && !binOp);
|
||||
increment = -1;
|
||||
break;
|
||||
case EOpAddAssign:
|
||||
ASSERT(!unOp && binOp);
|
||||
increment = EvaluateIntConstant(incrementNode);
|
||||
break;
|
||||
case EOpSubAssign:
|
||||
ASSERT(!unOp && binOp);
|
||||
increment = - EvaluateIntConstant(incrementNode);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
return increment;
|
||||
}
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
TLoopIndexInfo::TLoopIndexInfo()
|
||||
: mId(-1),
|
||||
mType(EbtVoid),
|
||||
mInitValue(0),
|
||||
mStopValue(0),
|
||||
mIncrementValue(0),
|
||||
mOp(EOpNull),
|
||||
mCurrentValue(0)
|
||||
{
|
||||
}
|
||||
|
||||
void TLoopIndexInfo::fillInfo(TIntermLoop *node)
|
||||
{
|
||||
if (node == NULL)
|
||||
return;
|
||||
|
||||
// Here we assume all the operations are valid, because the loop node is
|
||||
// already validated in ValidateLimitations.
|
||||
TIntermSequence &declSeq =
|
||||
node->getInit()->getAsAggregate()->getSequence();
|
||||
TIntermBinary *declInit = declSeq[0]->getAsBinaryNode();
|
||||
TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
|
||||
|
||||
mId = symbol->getId();
|
||||
mType = symbol->getBasicType();
|
||||
|
||||
if (mType == EbtInt)
|
||||
{
|
||||
TIntermConstantUnion* initNode = declInit->getRight()->getAsConstantUnion();
|
||||
mInitValue = EvaluateIntConstant(initNode);
|
||||
mCurrentValue = mInitValue;
|
||||
mIncrementValue = GetLoopIntIncrement(node);
|
||||
|
||||
TIntermBinary* binOp = node->getCondition()->getAsBinaryNode();
|
||||
mStopValue = EvaluateIntConstant(
|
||||
binOp->getRight()->getAsConstantUnion());
|
||||
mOp = binOp->getOp();
|
||||
}
|
||||
}
|
||||
|
||||
bool TLoopIndexInfo::satisfiesLoopCondition() const
|
||||
{
|
||||
// Relational operator is one of: > >= < <= == or !=.
|
||||
switch (mOp)
|
||||
{
|
||||
case EOpEqual:
|
||||
return (mCurrentValue == mStopValue);
|
||||
case EOpNotEqual:
|
||||
return (mCurrentValue != mStopValue);
|
||||
case EOpLessThan:
|
||||
return (mCurrentValue < mStopValue);
|
||||
case EOpGreaterThan:
|
||||
return (mCurrentValue > mStopValue);
|
||||
case EOpLessThanEqual:
|
||||
return (mCurrentValue <= mStopValue);
|
||||
case EOpGreaterThanEqual:
|
||||
return (mCurrentValue >= mStopValue);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
TLoopInfo::TLoopInfo()
|
||||
: loop(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
TLoopInfo::TLoopInfo(TIntermLoop *node)
|
||||
: loop(node)
|
||||
{
|
||||
index.fillInfo(node);
|
||||
}
|
||||
|
||||
TIntermLoop *TLoopStack::findLoop(TIntermSymbol *symbol)
|
||||
{
|
||||
if (!symbol)
|
||||
return NULL;
|
||||
for (iterator iter = begin(); iter != end(); ++iter)
|
||||
{
|
||||
if (iter->index.getId() == symbol->getId())
|
||||
return iter->loop;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TLoopIndexInfo *TLoopStack::getIndexInfo(TIntermSymbol *symbol)
|
||||
{
|
||||
if (!symbol)
|
||||
return NULL;
|
||||
for (iterator iter = begin(); iter != end(); ++iter)
|
||||
{
|
||||
if (iter->index.getId() == symbol->getId())
|
||||
return &(iter->index);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TLoopStack::step()
|
||||
{
|
||||
ASSERT(!empty());
|
||||
rbegin()->index.step();
|
||||
}
|
||||
|
||||
bool TLoopStack::satisfiesLoopCondition()
|
||||
{
|
||||
ASSERT(!empty());
|
||||
return rbegin()->index.satisfiesLoopCondition();
|
||||
}
|
||||
|
||||
bool TLoopStack::needsToReplaceSymbolWithValue(TIntermSymbol *symbol)
|
||||
{
|
||||
TIntermLoop *loop = findLoop(symbol);
|
||||
return loop && loop->getUnrollFlag();
|
||||
}
|
||||
|
||||
int TLoopStack::getLoopIndexValue(TIntermSymbol *symbol)
|
||||
{
|
||||
TLoopIndexInfo *info = getIndexInfo(symbol);
|
||||
ASSERT(info);
|
||||
return info->getCurrentValue();
|
||||
}
|
||||
|
||||
void TLoopStack::push(TIntermLoop *loop)
|
||||
{
|
||||
TLoopInfo info(loop);
|
||||
push_back(info);
|
||||
}
|
||||
|
||||
void TLoopStack::pop()
|
||||
{
|
||||
pop_back();
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// Copyright (c) 2002-2014 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_LOOP_INFO_H_
|
||||
#define COMPILER_TRANSLATOR_LOOP_INFO_H_
|
||||
|
||||
#include "compiler/translator/intermediate.h"
|
||||
|
||||
class TLoopIndexInfo
|
||||
{
|
||||
public:
|
||||
TLoopIndexInfo();
|
||||
|
||||
// If type is EbtInt, fill all fields of the structure with info
|
||||
// extracted from a loop node.
|
||||
// If type is not EbtInt, only fill id and type.
|
||||
void fillInfo(TIntermLoop *node);
|
||||
|
||||
int getId() const { return mId; }
|
||||
void setId(int id) { mId = id; }
|
||||
TBasicType getType() const { return mType; }
|
||||
void setType(TBasicType type) { mType = type; }
|
||||
int getCurrentValue() const { return mCurrentValue; }
|
||||
|
||||
void step() { mCurrentValue += mIncrementValue; }
|
||||
|
||||
// Check if the current value satisfies the loop condition.
|
||||
bool satisfiesLoopCondition() const;
|
||||
|
||||
private:
|
||||
int mId;
|
||||
TBasicType mType; // Either EbtInt or EbtFloat
|
||||
|
||||
// Below fields are only valid if the index's type is int.
|
||||
int mInitValue;
|
||||
int mStopValue;
|
||||
int mIncrementValue;
|
||||
TOperator mOp;
|
||||
int mCurrentValue;
|
||||
};
|
||||
|
||||
struct TLoopInfo
|
||||
{
|
||||
TLoopIndexInfo index;
|
||||
TIntermLoop *loop;
|
||||
|
||||
TLoopInfo();
|
||||
TLoopInfo(TIntermLoop *node);
|
||||
};
|
||||
|
||||
class TLoopStack : public TVector<TLoopInfo>
|
||||
{
|
||||
public:
|
||||
// Search loop stack for a loop whose index matches the input symbol.
|
||||
TIntermLoop *findLoop(TIntermSymbol *symbol);
|
||||
|
||||
// Find the loop index info in the loop stack by the input symbol.
|
||||
TLoopIndexInfo *getIndexInfo(TIntermSymbol *symbol);
|
||||
|
||||
// Update the currentValue for the next loop iteration.
|
||||
void step();
|
||||
|
||||
// Return false if loop condition is no longer satisfied.
|
||||
bool satisfiesLoopCondition();
|
||||
|
||||
// Check if the symbol is the index of a loop that's unrolled.
|
||||
bool needsToReplaceSymbolWithValue(TIntermSymbol *symbol);
|
||||
|
||||
// Return the current value of a given loop index symbol.
|
||||
int getLoopIndexValue(TIntermSymbol *symbol);
|
||||
|
||||
void push(TIntermLoop *info);
|
||||
void pop();
|
||||
};
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_LOOP_INDEX_H_
|
||||
|
|
@ -161,8 +161,8 @@ const ConstantUnion* TOutputGLSLBase::writeConstantUnion(const TType& type,
|
|||
void TOutputGLSLBase::visitSymbol(TIntermSymbol* node)
|
||||
{
|
||||
TInfoSinkBase& out = objSink();
|
||||
if (mLoopUnroll.NeedsToReplaceSymbolWithValue(node))
|
||||
out << mLoopUnroll.GetLoopIndexValue(node);
|
||||
if (mLoopUnrollStack.needsToReplaceSymbolWithValue(node))
|
||||
out << mLoopUnrollStack.getLoopIndexValue(node);
|
||||
else
|
||||
out << hashVariableName(node->getSymbol());
|
||||
|
||||
|
@ -645,7 +645,8 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
|
|||
TLoopType loopType = node->getType();
|
||||
if (loopType == ELoopFor) // for loop
|
||||
{
|
||||
if (!node->getUnrollFlag()) {
|
||||
if (!node->getUnrollFlag())
|
||||
{
|
||||
out << "for (";
|
||||
if (node->getInit())
|
||||
node->getInit()->traverse(this);
|
||||
|
@ -659,6 +660,18 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
|
|||
node->getExpression()->traverse(this);
|
||||
out << ")\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Need to put a one-iteration loop here to handle break.
|
||||
TIntermSequence &declSeq =
|
||||
node->getInit()->getAsAggregate()->getSequence();
|
||||
TIntermSymbol *indexSymbol =
|
||||
declSeq[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
|
||||
TString name = hashVariableName(indexSymbol->getSymbol());
|
||||
out << "for (int " << name << " = 0; "
|
||||
<< name << " < 1; "
|
||||
<< "++" << name << ")\n";
|
||||
}
|
||||
}
|
||||
else if (loopType == ELoopWhile) // while loop
|
||||
{
|
||||
|
@ -676,15 +689,15 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
|
|||
// Loop body.
|
||||
if (node->getUnrollFlag())
|
||||
{
|
||||
TLoopIndexInfo indexInfo;
|
||||
mLoopUnroll.FillLoopIndexInfo(node, indexInfo);
|
||||
mLoopUnroll.Push(indexInfo);
|
||||
while (mLoopUnroll.SatisfiesLoopCondition())
|
||||
out << "{\n";
|
||||
mLoopUnrollStack.push(node);
|
||||
while (mLoopUnrollStack.satisfiesLoopCondition())
|
||||
{
|
||||
visitCodeBlock(node->getBody());
|
||||
mLoopUnroll.Step();
|
||||
mLoopUnrollStack.step();
|
||||
}
|
||||
mLoopUnroll.Pop();
|
||||
mLoopUnrollStack.pop();
|
||||
out << "}\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
#include <set>
|
||||
|
||||
#include "compiler/translator/ForLoopUnroll.h"
|
||||
#include "compiler/translator/intermediate.h"
|
||||
#include "compiler/translator/LoopInfo.h"
|
||||
#include "compiler/translator/ParseContext.h"
|
||||
|
||||
class TOutputGLSLBase : public TIntermTraverser
|
||||
|
@ -65,7 +65,8 @@ private:
|
|||
typedef std::set<TString> DeclaredStructs;
|
||||
DeclaredStructs mDeclaredStructs;
|
||||
|
||||
ForLoopUnroll mLoopUnroll;
|
||||
// Stack of loops that need to be unrolled.
|
||||
TLoopStack mLoopUnrollStack;
|
||||
|
||||
ShArrayIndexClampingStrategy mClampingStrategy;
|
||||
|
||||
|
|
|
@ -9,25 +9,8 @@
|
|||
#include "compiler/translator/InitializeParseContext.h"
|
||||
#include "compiler/translator/ParseContext.h"
|
||||
|
||||
namespace {
|
||||
bool IsLoopIndex(const TIntermSymbol* symbol, const TLoopStack& stack) {
|
||||
for (TLoopStack::const_iterator i = stack.begin(); i != stack.end(); ++i) {
|
||||
if (i->index.id == symbol->getId())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MarkLoopForUnroll(const TIntermSymbol* symbol, TLoopStack& stack) {
|
||||
for (TLoopStack::iterator i = stack.begin(); i != stack.end(); ++i) {
|
||||
if (i->index.id == symbol->getId()) {
|
||||
ASSERT(i->loop != NULL);
|
||||
i->loop->setUnrollFlag(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
namespace
|
||||
{
|
||||
|
||||
// Traverses a node to check if it represents a constant index expression.
|
||||
// Definition:
|
||||
|
@ -38,114 +21,60 @@ void MarkLoopForUnroll(const TIntermSymbol* symbol, TLoopStack& stack) {
|
|||
// - Constant expressions
|
||||
// - Loop indices as defined in section 4
|
||||
// - Expressions composed of both of the above
|
||||
class ValidateConstIndexExpr : public TIntermTraverser {
|
||||
public:
|
||||
ValidateConstIndexExpr(const TLoopStack& stack)
|
||||
class ValidateConstIndexExpr : public TIntermTraverser
|
||||
{
|
||||
public:
|
||||
ValidateConstIndexExpr(TLoopStack& stack)
|
||||
: mValid(true), mLoopStack(stack) {}
|
||||
|
||||
// Returns true if the parsed node represents a constant index expression.
|
||||
bool isValid() const { return mValid; }
|
||||
|
||||
virtual void visitSymbol(TIntermSymbol* symbol) {
|
||||
virtual void visitSymbol(TIntermSymbol *symbol)
|
||||
{
|
||||
// Only constants and loop indices are allowed in a
|
||||
// constant index expression.
|
||||
if (mValid) {
|
||||
if (mValid)
|
||||
{
|
||||
mValid = (symbol->getQualifier() == EvqConst) ||
|
||||
IsLoopIndex(symbol, mLoopStack);
|
||||
(mLoopStack.findLoop(symbol));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
bool mValid;
|
||||
const TLoopStack& mLoopStack;
|
||||
};
|
||||
|
||||
// Traverses a node to check if it uses a loop index.
|
||||
// If an int loop index is used in its body as a sampler array index,
|
||||
// mark the loop for unroll.
|
||||
class ValidateLoopIndexExpr : public TIntermTraverser {
|
||||
public:
|
||||
ValidateLoopIndexExpr(TLoopStack& stack)
|
||||
: mUsesFloatLoopIndex(false),
|
||||
mUsesIntLoopIndex(false),
|
||||
mLoopStack(stack) {}
|
||||
|
||||
bool usesFloatLoopIndex() const { return mUsesFloatLoopIndex; }
|
||||
bool usesIntLoopIndex() const { return mUsesIntLoopIndex; }
|
||||
|
||||
virtual void visitSymbol(TIntermSymbol* symbol) {
|
||||
if (IsLoopIndex(symbol, mLoopStack)) {
|
||||
switch (symbol->getBasicType()) {
|
||||
case EbtFloat:
|
||||
mUsesFloatLoopIndex = true;
|
||||
break;
|
||||
case EbtUInt:
|
||||
mUsesIntLoopIndex = true;
|
||||
MarkLoopForUnroll(symbol, mLoopStack);
|
||||
break;
|
||||
case EbtInt:
|
||||
mUsesIntLoopIndex = true;
|
||||
MarkLoopForUnroll(symbol, mLoopStack);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool mUsesFloatLoopIndex;
|
||||
bool mUsesIntLoopIndex;
|
||||
TLoopStack& mLoopStack;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
} // namespace anonymous
|
||||
|
||||
ValidateLimitations::ValidateLimitations(ShShaderType shaderType,
|
||||
TInfoSinkBase& sink)
|
||||
TInfoSinkBase &sink)
|
||||
: mShaderType(shaderType),
|
||||
mSink(sink),
|
||||
mNumErrors(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool ValidateLimitations::visitBinary(Visit, TIntermBinary* node)
|
||||
bool ValidateLimitations::visitBinary(Visit, TIntermBinary *node)
|
||||
{
|
||||
// Check if loop index is modified in the loop body.
|
||||
validateOperation(node, node->getLeft());
|
||||
|
||||
// Check indexing.
|
||||
switch (node->getOp()) {
|
||||
switch (node->getOp())
|
||||
{
|
||||
case EOpIndexDirect:
|
||||
validateIndexing(node);
|
||||
break;
|
||||
case EOpIndexIndirect:
|
||||
#if defined(__APPLE__)
|
||||
// Loop unrolling is a work-around for a Mac Cg compiler bug where it
|
||||
// crashes when a sampler array's index is also the loop index.
|
||||
// Once Apple fixes this bug, we should remove the code in this CL.
|
||||
// See http://codereview.appspot.com/4331048/.
|
||||
if ((node->getLeft() != NULL) && (node->getRight() != NULL) &&
|
||||
(node->getLeft()->getAsSymbolNode())) {
|
||||
TIntermSymbol* symbol = node->getLeft()->getAsSymbolNode();
|
||||
if (IsSampler(symbol->getBasicType()) && symbol->isArray()) {
|
||||
ValidateLoopIndexExpr validate(mLoopStack);
|
||||
node->getRight()->traverse(&validate);
|
||||
if (validate.usesFloatLoopIndex()) {
|
||||
error(node->getLine(),
|
||||
"sampler array index is float loop index",
|
||||
"for");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
validateIndexing(node);
|
||||
break;
|
||||
default: break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::visitUnary(Visit, TIntermUnary* node)
|
||||
bool ValidateLimitations::visitUnary(Visit, TIntermUnary *node)
|
||||
{
|
||||
// Check if loop index is modified in the loop body.
|
||||
validateOperation(node, node->getOperand());
|
||||
|
@ -153,7 +82,7 @@ bool ValidateLimitations::visitUnary(Visit, TIntermUnary* node)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate* node)
|
||||
bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate *node)
|
||||
{
|
||||
switch (node->getOp()) {
|
||||
case EOpFunctionCall:
|
||||
|
@ -165,22 +94,20 @@ bool ValidateLimitations::visitAggregate(Visit, TIntermAggregate* node)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::visitLoop(Visit, TIntermLoop* node)
|
||||
bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node)
|
||||
{
|
||||
if (!validateLoopType(node))
|
||||
return false;
|
||||
|
||||
TLoopInfo info;
|
||||
memset(&info, 0, sizeof(TLoopInfo));
|
||||
info.loop = node;
|
||||
if (!validateForLoopHeader(node, &info))
|
||||
if (!validateForLoopHeader(node))
|
||||
return false;
|
||||
|
||||
TIntermNode* body = node->getBody();
|
||||
if (body != NULL) {
|
||||
mLoopStack.push_back(info);
|
||||
TIntermNode *body = node->getBody();
|
||||
if (body != NULL)
|
||||
{
|
||||
mLoopStack.push(node);
|
||||
body->traverse(this);
|
||||
mLoopStack.pop_back();
|
||||
mLoopStack.pop();
|
||||
}
|
||||
|
||||
// The loop is fully processed - no need to visit children.
|
||||
|
@ -188,7 +115,7 @@ bool ValidateLimitations::visitLoop(Visit, TIntermLoop* node)
|
|||
}
|
||||
|
||||
void ValidateLimitations::error(TSourceLoc loc,
|
||||
const char *reason, const char* token)
|
||||
const char *reason, const char *token)
|
||||
{
|
||||
mSink.prefix(EPrefixError);
|
||||
mSink.location(loc);
|
||||
|
@ -201,12 +128,13 @@ bool ValidateLimitations::withinLoopBody() const
|
|||
return !mLoopStack.empty();
|
||||
}
|
||||
|
||||
bool ValidateLimitations::isLoopIndex(const TIntermSymbol* symbol) const
|
||||
bool ValidateLimitations::isLoopIndex(TIntermSymbol *symbol)
|
||||
{
|
||||
return IsLoopIndex(symbol, mLoopStack);
|
||||
return mLoopStack.findLoop(symbol) != NULL;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::validateLoopType(TIntermLoop* node) {
|
||||
bool ValidateLimitations::validateLoopType(TIntermLoop *node)
|
||||
{
|
||||
TLoopType type = node->getType();
|
||||
if (type == ELoopFor)
|
||||
return true;
|
||||
|
@ -218,8 +146,7 @@ bool ValidateLimitations::validateLoopType(TIntermLoop* node) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::validateForLoopHeader(TIntermLoop* node,
|
||||
TLoopInfo* info)
|
||||
bool ValidateLimitations::validateForLoopHeader(TIntermLoop *node)
|
||||
{
|
||||
ASSERT(node->getType() == ELoopFor);
|
||||
|
||||
|
@ -227,74 +154,80 @@ bool ValidateLimitations::validateForLoopHeader(TIntermLoop* node,
|
|||
// The for statement has the form:
|
||||
// for ( init-declaration ; condition ; expression ) statement
|
||||
//
|
||||
if (!validateForLoopInit(node, info))
|
||||
int indexSymbolId = validateForLoopInit(node);
|
||||
if (indexSymbolId < 0)
|
||||
return false;
|
||||
if (!validateForLoopCond(node, info))
|
||||
if (!validateForLoopCond(node, indexSymbolId))
|
||||
return false;
|
||||
if (!validateForLoopExpr(node, info))
|
||||
if (!validateForLoopExpr(node, indexSymbolId))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::validateForLoopInit(TIntermLoop* node,
|
||||
TLoopInfo* info)
|
||||
int ValidateLimitations::validateForLoopInit(TIntermLoop *node)
|
||||
{
|
||||
TIntermNode* init = node->getInit();
|
||||
if (init == NULL) {
|
||||
TIntermNode *init = node->getInit();
|
||||
if (init == NULL)
|
||||
{
|
||||
error(node->getLine(), "Missing init declaration", "for");
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// init-declaration has the form:
|
||||
// type-specifier identifier = constant-expression
|
||||
//
|
||||
TIntermAggregate* decl = init->getAsAggregate();
|
||||
if ((decl == NULL) || (decl->getOp() != EOpDeclaration)) {
|
||||
TIntermAggregate *decl = init->getAsAggregate();
|
||||
if ((decl == NULL) || (decl->getOp() != EOpDeclaration))
|
||||
{
|
||||
error(init->getLine(), "Invalid init declaration", "for");
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
// To keep things simple do not allow declaration list.
|
||||
TIntermSequence& declSeq = decl->getSequence();
|
||||
if (declSeq.size() != 1) {
|
||||
TIntermSequence &declSeq = decl->getSequence();
|
||||
if (declSeq.size() != 1)
|
||||
{
|
||||
error(decl->getLine(), "Invalid init declaration", "for");
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
TIntermBinary* declInit = declSeq[0]->getAsBinaryNode();
|
||||
if ((declInit == NULL) || (declInit->getOp() != EOpInitialize)) {
|
||||
TIntermBinary *declInit = declSeq[0]->getAsBinaryNode();
|
||||
if ((declInit == NULL) || (declInit->getOp() != EOpInitialize))
|
||||
{
|
||||
error(decl->getLine(), "Invalid init declaration", "for");
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
TIntermSymbol* symbol = declInit->getLeft()->getAsSymbolNode();
|
||||
if (symbol == NULL) {
|
||||
TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
|
||||
if (symbol == NULL)
|
||||
{
|
||||
error(declInit->getLine(), "Invalid init declaration", "for");
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
// The loop index has type int or float.
|
||||
TBasicType type = symbol->getBasicType();
|
||||
if ((type != EbtInt) && (type != EbtUInt) && (type != EbtFloat)) {
|
||||
error(symbol->getLine(),
|
||||
"Invalid type for loop index", getBasicString(type));
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
// The loop index is initialized with constant expression.
|
||||
if (!isConstExpr(declInit->getRight())) {
|
||||
if (!isConstExpr(declInit->getRight()))
|
||||
{
|
||||
error(declInit->getLine(),
|
||||
"Loop index cannot be initialized with non-constant expression",
|
||||
symbol->getSymbol().c_str());
|
||||
return false;
|
||||
return -1;
|
||||
}
|
||||
|
||||
info->index.id = symbol->getId();
|
||||
return true;
|
||||
return symbol->getId();
|
||||
}
|
||||
|
||||
bool ValidateLimitations::validateForLoopCond(TIntermLoop* node,
|
||||
TLoopInfo* info)
|
||||
bool ValidateLimitations::validateForLoopCond(TIntermLoop *node,
|
||||
int indexSymbolId)
|
||||
{
|
||||
TIntermNode* cond = node->getCondition();
|
||||
if (cond == NULL) {
|
||||
TIntermNode *cond = node->getCondition();
|
||||
if (cond == NULL)
|
||||
{
|
||||
error(node->getLine(), "Missing condition", "for");
|
||||
return false;
|
||||
}
|
||||
|
@ -302,24 +235,28 @@ bool ValidateLimitations::validateForLoopCond(TIntermLoop* node,
|
|||
// condition has the form:
|
||||
// loop_index relational_operator constant_expression
|
||||
//
|
||||
TIntermBinary* binOp = cond->getAsBinaryNode();
|
||||
if (binOp == NULL) {
|
||||
TIntermBinary *binOp = cond->getAsBinaryNode();
|
||||
if (binOp == NULL)
|
||||
{
|
||||
error(node->getLine(), "Invalid condition", "for");
|
||||
return false;
|
||||
}
|
||||
// Loop index should be to the left of relational operator.
|
||||
TIntermSymbol* symbol = binOp->getLeft()->getAsSymbolNode();
|
||||
if (symbol == NULL) {
|
||||
TIntermSymbol *symbol = binOp->getLeft()->getAsSymbolNode();
|
||||
if (symbol == NULL)
|
||||
{
|
||||
error(binOp->getLine(), "Invalid condition", "for");
|
||||
return false;
|
||||
}
|
||||
if (symbol->getId() != info->index.id) {
|
||||
if (symbol->getId() != indexSymbolId)
|
||||
{
|
||||
error(symbol->getLine(),
|
||||
"Expected loop index", symbol->getSymbol().c_str());
|
||||
return false;
|
||||
}
|
||||
// Relational operator is one of: > >= < <= == or !=.
|
||||
switch (binOp->getOp()) {
|
||||
switch (binOp->getOp())
|
||||
{
|
||||
case EOpEqual:
|
||||
case EOpNotEqual:
|
||||
case EOpLessThan:
|
||||
|
@ -334,7 +271,8 @@ bool ValidateLimitations::validateForLoopCond(TIntermLoop* node,
|
|||
break;
|
||||
}
|
||||
// Loop index must be compared with a constant.
|
||||
if (!isConstExpr(binOp->getRight())) {
|
||||
if (!isConstExpr(binOp->getRight()))
|
||||
{
|
||||
error(binOp->getLine(),
|
||||
"Loop index cannot be compared with non-constant expression",
|
||||
symbol->getSymbol().c_str());
|
||||
|
@ -344,11 +282,12 @@ bool ValidateLimitations::validateForLoopCond(TIntermLoop* node,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::validateForLoopExpr(TIntermLoop* node,
|
||||
TLoopInfo* info)
|
||||
bool ValidateLimitations::validateForLoopExpr(TIntermLoop *node,
|
||||
int indexSymbolId)
|
||||
{
|
||||
TIntermNode* expr = node->getExpression();
|
||||
if (expr == NULL) {
|
||||
TIntermNode *expr = node->getExpression();
|
||||
if (expr == NULL)
|
||||
{
|
||||
error(node->getLine(), "Missing expression", "for");
|
||||
return false;
|
||||
}
|
||||
|
@ -362,50 +301,58 @@ bool ValidateLimitations::validateForLoopExpr(TIntermLoop* node,
|
|||
// --loop_index
|
||||
// The last two forms are not specified in the spec, but I am assuming
|
||||
// its an oversight.
|
||||
TIntermUnary* unOp = expr->getAsUnaryNode();
|
||||
TIntermBinary* binOp = unOp ? NULL : expr->getAsBinaryNode();
|
||||
TIntermUnary *unOp = expr->getAsUnaryNode();
|
||||
TIntermBinary *binOp = unOp ? NULL : expr->getAsBinaryNode();
|
||||
|
||||
TOperator op = EOpNull;
|
||||
TIntermSymbol* symbol = NULL;
|
||||
if (unOp != NULL) {
|
||||
TIntermSymbol *symbol = NULL;
|
||||
if (unOp != NULL)
|
||||
{
|
||||
op = unOp->getOp();
|
||||
symbol = unOp->getOperand()->getAsSymbolNode();
|
||||
} else if (binOp != NULL) {
|
||||
}
|
||||
else if (binOp != NULL)
|
||||
{
|
||||
op = binOp->getOp();
|
||||
symbol = binOp->getLeft()->getAsSymbolNode();
|
||||
}
|
||||
|
||||
// The operand must be loop index.
|
||||
if (symbol == NULL) {
|
||||
if (symbol == NULL)
|
||||
{
|
||||
error(expr->getLine(), "Invalid expression", "for");
|
||||
return false;
|
||||
}
|
||||
if (symbol->getId() != info->index.id) {
|
||||
if (symbol->getId() != indexSymbolId)
|
||||
{
|
||||
error(symbol->getLine(),
|
||||
"Expected loop index", symbol->getSymbol().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// The operator is one of: ++ -- += -=.
|
||||
switch (op) {
|
||||
case EOpPostIncrement:
|
||||
case EOpPostDecrement:
|
||||
case EOpPreIncrement:
|
||||
case EOpPreDecrement:
|
||||
ASSERT((unOp != NULL) && (binOp == NULL));
|
||||
break;
|
||||
case EOpAddAssign:
|
||||
case EOpSubAssign:
|
||||
ASSERT((unOp == NULL) && (binOp != NULL));
|
||||
break;
|
||||
default:
|
||||
error(expr->getLine(), "Invalid operator", getOperatorString(op));
|
||||
return false;
|
||||
switch (op)
|
||||
{
|
||||
case EOpPostIncrement:
|
||||
case EOpPostDecrement:
|
||||
case EOpPreIncrement:
|
||||
case EOpPreDecrement:
|
||||
ASSERT((unOp != NULL) && (binOp == NULL));
|
||||
break;
|
||||
case EOpAddAssign:
|
||||
case EOpSubAssign:
|
||||
ASSERT((unOp == NULL) && (binOp != NULL));
|
||||
break;
|
||||
default:
|
||||
error(expr->getLine(), "Invalid operator", getOperatorString(op));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Loop index must be incremented/decremented with a constant.
|
||||
if (binOp != NULL) {
|
||||
if (!isConstExpr(binOp->getRight())) {
|
||||
if (binOp != NULL)
|
||||
{
|
||||
if (!isConstExpr(binOp->getRight()))
|
||||
{
|
||||
error(binOp->getLine(),
|
||||
"Loop index cannot be modified by non-constant expression",
|
||||
symbol->getSymbol().c_str());
|
||||
|
@ -416,7 +363,7 @@ bool ValidateLimitations::validateForLoopExpr(TIntermLoop* node,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::validateFunctionCall(TIntermAggregate* node)
|
||||
bool ValidateLimitations::validateFunctionCall(TIntermAggregate *node)
|
||||
{
|
||||
ASSERT(node->getOp() == EOpFunctionCall);
|
||||
|
||||
|
@ -428,8 +375,9 @@ bool ValidateLimitations::validateFunctionCall(TIntermAggregate* node)
|
|||
typedef std::vector<size_t> ParamIndex;
|
||||
ParamIndex pIndex;
|
||||
TIntermSequence& params = node->getSequence();
|
||||
for (TIntermSequence::size_type i = 0; i < params.size(); ++i) {
|
||||
TIntermSymbol* symbol = params[i]->getAsSymbolNode();
|
||||
for (TIntermSequence::size_type i = 0; i < params.size(); ++i)
|
||||
{
|
||||
TIntermSymbol *symbol = params[i]->getAsSymbolNode();
|
||||
if (symbol && isLoopIndex(symbol))
|
||||
pIndex.push_back(i);
|
||||
}
|
||||
|
@ -442,12 +390,14 @@ bool ValidateLimitations::validateFunctionCall(TIntermAggregate* node)
|
|||
TSymbolTable& symbolTable = GetGlobalParseContext()->symbolTable;
|
||||
TSymbol* symbol = symbolTable.find(node->getName(), GetGlobalParseContext()->shaderVersion);
|
||||
ASSERT(symbol && symbol->isFunction());
|
||||
TFunction* function = static_cast<TFunction*>(symbol);
|
||||
TFunction *function = static_cast<TFunction *>(symbol);
|
||||
for (ParamIndex::const_iterator i = pIndex.begin();
|
||||
i != pIndex.end(); ++i) {
|
||||
const TParameter& param = function->getParam(*i);
|
||||
i != pIndex.end(); ++i)
|
||||
{
|
||||
const TParameter ¶m = function->getParam(*i);
|
||||
TQualifier qual = param.type->getQualifier();
|
||||
if ((qual == EvqOut) || (qual == EvqInOut)) {
|
||||
if ((qual == EvqOut) || (qual == EvqInOut))
|
||||
{
|
||||
error(params[*i]->getLine(),
|
||||
"Loop index cannot be used as argument to a function out or inout parameter",
|
||||
params[*i]->getAsSymbolNode()->getSymbol().c_str());
|
||||
|
@ -458,14 +408,16 @@ bool ValidateLimitations::validateFunctionCall(TIntermAggregate* node)
|
|||
return valid;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::validateOperation(TIntermOperator* node,
|
||||
TIntermNode* operand) {
|
||||
bool ValidateLimitations::validateOperation(TIntermOperator *node,
|
||||
TIntermNode* operand)
|
||||
{
|
||||
// Check if loop index is modified in the loop body.
|
||||
if (!withinLoopBody() || !node->isAssignment())
|
||||
return true;
|
||||
|
||||
const TIntermSymbol* symbol = operand->getAsSymbolNode();
|
||||
if (symbol && isLoopIndex(symbol)) {
|
||||
TIntermSymbol *symbol = operand->getAsSymbolNode();
|
||||
if (symbol && isLoopIndex(symbol))
|
||||
{
|
||||
error(node->getLine(),
|
||||
"Loop index cannot be statically assigned to within the body of the loop",
|
||||
symbol->getSymbol().c_str());
|
||||
|
@ -473,13 +425,13 @@ bool ValidateLimitations::validateOperation(TIntermOperator* node,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::isConstExpr(TIntermNode* node)
|
||||
bool ValidateLimitations::isConstExpr(TIntermNode *node)
|
||||
{
|
||||
ASSERT(node != NULL);
|
||||
return node->getAsConstantUnion() != NULL;
|
||||
}
|
||||
|
||||
bool ValidateLimitations::isConstIndexExpr(TIntermNode* node)
|
||||
bool ValidateLimitations::isConstIndexExpr(TIntermNode *node)
|
||||
{
|
||||
ASSERT(node != NULL);
|
||||
|
||||
|
@ -488,13 +440,13 @@ bool ValidateLimitations::isConstIndexExpr(TIntermNode* node)
|
|||
return validate.isValid();
|
||||
}
|
||||
|
||||
bool ValidateLimitations::validateIndexing(TIntermBinary* node)
|
||||
bool ValidateLimitations::validateIndexing(TIntermBinary *node)
|
||||
{
|
||||
ASSERT((node->getOp() == EOpIndexDirect) ||
|
||||
(node->getOp() == EOpIndexIndirect));
|
||||
|
||||
bool valid = true;
|
||||
TIntermTyped* index = node->getRight();
|
||||
TIntermTyped *index = node->getRight();
|
||||
// The index expression must have integral type.
|
||||
if (!index->isScalarInt()) {
|
||||
error(index->getLine(),
|
||||
|
@ -504,10 +456,11 @@ bool ValidateLimitations::validateIndexing(TIntermBinary* node)
|
|||
}
|
||||
// The index expession must be a constant-index-expression unless
|
||||
// the operand is a uniform in a vertex shader.
|
||||
TIntermTyped* operand = node->getLeft();
|
||||
TIntermTyped *operand = node->getLeft();
|
||||
bool skip = (mShaderType == SH_VERTEX_SHADER) &&
|
||||
(operand->getQualifier() == EvqUniform);
|
||||
if (!skip && !isConstIndexExpr(index)) {
|
||||
if (!skip && !isConstIndexExpr(index))
|
||||
{
|
||||
error(index->getLine(), "Index expression must be constant", "[]");
|
||||
valid = false;
|
||||
}
|
||||
|
|
|
@ -6,53 +6,50 @@
|
|||
|
||||
#include "GLSLANG/ShaderLang.h"
|
||||
#include "compiler/translator/intermediate.h"
|
||||
#include "compiler/translator/LoopInfo.h"
|
||||
|
||||
class TInfoSinkBase;
|
||||
|
||||
struct TLoopInfo {
|
||||
struct TIndex {
|
||||
int id; // symbol id.
|
||||
} index;
|
||||
TIntermLoop* loop;
|
||||
};
|
||||
typedef TVector<TLoopInfo> TLoopStack;
|
||||
|
||||
// Traverses intermediate tree to ensure that the shader does not exceed the
|
||||
// minimum functionality mandated in GLSL 1.0 spec, Appendix A.
|
||||
class ValidateLimitations : public TIntermTraverser {
|
||||
public:
|
||||
ValidateLimitations(ShShaderType shaderType, TInfoSinkBase& sink);
|
||||
class ValidateLimitations : public TIntermTraverser
|
||||
{
|
||||
public:
|
||||
ValidateLimitations(ShShaderType shaderType, TInfoSinkBase &sink);
|
||||
|
||||
int numErrors() const { return mNumErrors; }
|
||||
|
||||
virtual bool visitBinary(Visit, TIntermBinary*);
|
||||
virtual bool visitUnary(Visit, TIntermUnary*);
|
||||
virtual bool visitAggregate(Visit, TIntermAggregate*);
|
||||
virtual bool visitLoop(Visit, TIntermLoop*);
|
||||
virtual bool visitBinary(Visit, TIntermBinary *);
|
||||
virtual bool visitUnary(Visit, TIntermUnary *);
|
||||
virtual bool visitAggregate(Visit, TIntermAggregate *);
|
||||
virtual bool visitLoop(Visit, TIntermLoop *);
|
||||
|
||||
private:
|
||||
void error(TSourceLoc loc, const char *reason, const char* token);
|
||||
private:
|
||||
void error(TSourceLoc loc, const char *reason, const char *token);
|
||||
|
||||
bool withinLoopBody() const;
|
||||
bool isLoopIndex(const TIntermSymbol* symbol) const;
|
||||
bool validateLoopType(TIntermLoop* node);
|
||||
bool validateForLoopHeader(TIntermLoop* node, TLoopInfo* info);
|
||||
bool validateForLoopInit(TIntermLoop* node, TLoopInfo* info);
|
||||
bool validateForLoopCond(TIntermLoop* node, TLoopInfo* info);
|
||||
bool validateForLoopExpr(TIntermLoop* node, TLoopInfo* info);
|
||||
bool isLoopIndex(TIntermSymbol *symbol);
|
||||
bool validateLoopType(TIntermLoop *node);
|
||||
|
||||
bool validateForLoopHeader(TIntermLoop *node);
|
||||
// If valid, return the index symbol id; Otherwise, return -1.
|
||||
int validateForLoopInit(TIntermLoop *node);
|
||||
bool validateForLoopCond(TIntermLoop *node, int indexSymbolId);
|
||||
bool validateForLoopExpr(TIntermLoop *node, int indexSymbolId);
|
||||
|
||||
// Returns true if none of the loop indices is used as the argument to
|
||||
// the given function out or inout parameter.
|
||||
bool validateFunctionCall(TIntermAggregate* node);
|
||||
bool validateOperation(TIntermOperator* node, TIntermNode* operand);
|
||||
bool validateFunctionCall(TIntermAggregate *node);
|
||||
bool validateOperation(TIntermOperator *node, TIntermNode *operand);
|
||||
|
||||
// Returns true if indexing does not exceed the minimum functionality
|
||||
// mandated in GLSL 1.0 spec, Appendix A, Section 5.
|
||||
bool isConstExpr(TIntermNode* node);
|
||||
bool isConstIndexExpr(TIntermNode* node);
|
||||
bool validateIndexing(TIntermBinary* node);
|
||||
bool isConstExpr(TIntermNode *node);
|
||||
bool isConstIndexExpr(TIntermNode *node);
|
||||
bool validateIndexing(TIntermBinary *node);
|
||||
|
||||
ShShaderType mShaderType;
|
||||
TInfoSinkBase& mSink;
|
||||
TInfoSinkBase &mSink;
|
||||
int mNumErrors;
|
||||
TLoopStack mLoopStack;
|
||||
};
|
||||
|
|
Загрузка…
Ссылка в новой задаче