Commit 550c600b by Zhenyao Mo

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/188079Reviewed-by: 's avatarZhenyao Mo <zmo@chromium.org> Tested-by: 's avatarZhenyao Mo <zmo@chromium.org> Conflicts: include/GLSLANG/ShaderLang.h src/compiler/translator/ValidateLimitations.cpp Change-Id: I546197bd7df1634ebccdd380be14c3250cd56151 Reviewed-on: https://chromium-review.googlesource.com/189061Reviewed-by: 's avatarShannon Woods <shannonwoods@chromium.org> Tested-by: 's avatarZhenyao Mo <zmo@chromium.org>
parent 2515c520
...@@ -170,9 +170,14 @@ typedef enum { ...@@ -170,9 +170,14 @@ typedef enum {
SH_SOURCE_PATH = 0x0020, SH_SOURCE_PATH = 0x0020,
SH_MAP_LONG_VARIABLE_NAMES = 0x0040, SH_MAP_LONG_VARIABLE_NAMES = 0x0040,
SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX = 0x0080, 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. // 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 // This is an experimental flag to enforce restrictions that aim to prevent
// timing attacks. // timing attacks.
...@@ -180,15 +185,15 @@ typedef enum { ...@@ -180,15 +185,15 @@ typedef enum {
// texture information via the timing channel. // texture information via the timing channel.
// To use this flag, you must compile the shader under the WebGL spec // To use this flag, you must compile the shader under the WebGL spec
// (using the SH_WEBGL_SPEC flag). // (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 // This flag prints the dependency graph that is used to enforce timing
// restrictions on fragment shaders. // restrictions on fragment shaders.
// This flag only has an effect if all of the following are true: // This flag only has an effect if all of the following are true:
// - The shader spec is SH_WEBGL_SPEC. // - The shader spec is SH_WEBGL_SPEC.
// - The compile options contain the SH_TIMING_RESTRICTIONS flag. // - The compile options contain the SH_TIMING_RESTRICTIONS flag.
// - The shader type is SH_FRAGMENT_SHADER. // - 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. // 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
...@@ -196,7 +201,7 @@ typedef enum { ...@@ -196,7 +201,7 @@ typedef enum {
// shaders. ShCheckVariablesWithinPackingLimits() lets embedders // shaders. ShCheckVariablesWithinPackingLimits() lets embedders
// enforce the packing restrictions for varying variables during // enforce the packing restrictions for varying variables during
// program link time. // program link time.
SH_ENFORCE_PACKING_RESTRICTIONS = 0x0800, SH_ENFORCE_PACKING_RESTRICTIONS = 0x1000,
// This flag ensures all indirect (expression-based) array indexing // This flag ensures all indirect (expression-based) array indexing
// is clamped to the bounds of the array. This ensures, for example, // is clamped to the bounds of the array. This ensures, for example,
...@@ -204,32 +209,32 @@ typedef enum { ...@@ -204,32 +209,32 @@ typedef enum {
// vec234, or mat234 type. The ShArrayIndexClampingStrategy enum, // vec234, or mat234 type. The ShArrayIndexClampingStrategy enum,
// specified in the ShBuiltInResources when constructing the // specified in the ShBuiltInResources when constructing the
// compiler, selects the strategy for the clamping implementation. // 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. // 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. // 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 // 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 // beginning of the vertex shader's main(), and has no effect in the
// fragment shader. It is intended as a workaround for drivers which // fragment shader. It is intended as a workaround for drivers which
// incorrectly fail to link programs if gl_Position is not written. // incorrectly fail to link programs if gl_Position is not written.
SH_INIT_GL_POSITION = 0x8000, SH_INIT_GL_POSITION = 0x10000,
// This flag replaces // This flag replaces
// "a && b" with "a ? b : false", // "a && b" with "a ? b : false",
// "a || b" with "a ? true : b". // "a || b" with "a ? true : b".
// This is to work around a MacOSX driver bug that |b| is executed // This is to work around a MacOSX driver bug that |b| is executed
// independent of |a|'s value. // 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 // This flag initializes varyings without static use in vertex shader
// at the beginning of main(), and has no effects in the fragment 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 // It is intended as a workaround for drivers which incorrectly optimize
// out such varyings and cause a link failure. // out such varyings and cause a link failure.
SH_INIT_VARYINGS_WITHOUT_STATIC_USE = 0x20000, SH_INIT_VARYINGS_WITHOUT_STATIC_USE = 0x40000,
} ShCompileOptions; } ShCompileOptions;
// Defines alternate strategies for implementing array index clamping. // Defines alternate strategies for implementing array index clamping.
......
...@@ -201,7 +201,21 @@ bool TCompiler::compile(const char* const shaderStrings[], ...@@ -201,7 +201,21 @@ bool TCompiler::compile(const char* const shaderStrings[],
// 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))
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. // Built-in function emulation needs to happen after validateLimitations pass.
if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS)) if (success && (compileOptions & SH_EMULATE_BUILT_IN_FUNCTIONS))
......
...@@ -6,210 +6,77 @@ ...@@ -6,210 +6,77 @@
#include "compiler/translator/ForLoopUnroll.h" #include "compiler/translator/ForLoopUnroll.h"
namespace { bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node)
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);
return true;
}
};
} // anonymous namepsace
void ForLoopUnroll::FillLoopIndexInfo(TIntermLoop* node, TLoopIndexInfo& info)
{ {
ASSERT(node->getType() == ELoopFor); if (mUnrollCondition != kSamplerArrayIndex)
ASSERT(node->getUnrollFlag()); return true;
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() // If a sampler array index is also the loop index,
{ // 1) if the index type is integer, mark the loop for unrolling;
ASSERT(mLoopIndexStack.size() > 0); // 2) if the index type if float, set a flag to later fail compile.
TLoopIndexInfo& info = mLoopIndexStack[mLoopIndexStack.size() - 1]; switch (node->getOp())
// Relational operator is one of: > >= < <= == or !=. {
switch (info.op) { case EOpIndexIndirect:
case EOpEqual: if (node->getLeft() != NULL && node->getRight() != NULL && node->getLeft()->getAsSymbolNode())
return (info.currentValue == info.stopValue); {
case EOpNotEqual: TIntermSymbol *symbol = node->getLeft()->getAsSymbolNode();
return (info.currentValue != info.stopValue); if (IsSampler(symbol->getBasicType()) && symbol->isArray() && !mLoopStack.empty())
case EOpLessThan: {
return (info.currentValue < info.stopValue); mVisitSamplerArrayIndexNodeInsideLoop = true;
case EOpGreaterThan: node->getRight()->traverse(this);
return (info.currentValue > info.stopValue); mVisitSamplerArrayIndexNodeInsideLoop = false;
case EOpLessThanEqual: // We have already visited all the children.
return (info.currentValue <= info.stopValue); return false;
case EOpGreaterThanEqual: }
return (info.currentValue >= info.stopValue); }
break;
default: default:
UNREACHABLE(); break;
} }
return false; return true;
} }
bool ForLoopUnroll::NeedsToReplaceSymbolWithValue(TIntermSymbol* symbol) bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node)
{ {
for (TVector<TLoopIndexInfo>::iterator i = mLoopIndexStack.begin(); if (mUnrollCondition == kIntegerIndex)
i != mLoopIndexStack.end(); {
++i) { // Check if loop index type is integer.
if (i->id == symbol->getId()) // This is called after ValidateLimitations pass, so all the calls
return true; // 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);
} }
return false;
}
int ForLoopUnroll::GetLoopIndexValue(TIntermSymbol* symbol) TIntermNode *body = node->getBody();
{ if (body != NULL)
for (TVector<TLoopIndexInfo>::iterator i = mLoopIndexStack.begin(); {
i != mLoopIndexStack.end(); mLoopStack.push(node);
++i) { body->traverse(this);
if (i->id == symbol->getId()) mLoopStack.pop();
return i->currentValue;
} }
UNREACHABLE(); // The loop is fully processed - no need to visit children.
return false; return false;
} }
void ForLoopUnroll::Push(TLoopIndexInfo& info) void ForLoopUnrollMarker::visitSymbol(TIntermSymbol* symbol)
{
mLoopIndexStack.push_back(info);
}
void ForLoopUnroll::Pop()
{ {
mLoopIndexStack.pop_back(); if (!mVisitSamplerArrayIndexNodeInsideLoop)
} return;
TIntermLoop *loop = mLoopStack.findLoop(symbol);
// static if (loop)
void ForLoopUnroll::MarkForLoopsWithIntegerIndicesForUnrolling( {
TIntermNode* root) switch (symbol->getBasicType())
{ {
ASSERT(root); case EbtFloat:
mSamplerArrayIndexIsFloatLoopIndex = true;
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;
break;
case EOpPostDecrement:
case EOpPreDecrement:
ASSERT((unOp != NULL) && (binOp == NULL));
increment = -1;
break;
case EOpAddAssign:
ASSERT((unOp == NULL) && (binOp != NULL));
increment = evaluateIntConstant(incrementNode);
break; break;
case EOpSubAssign: case EbtInt:
ASSERT((unOp == NULL) && (binOp != NULL)); loop->setUnrollFlag(true);
increment = - evaluateIntConstant(incrementNode);
break; break;
default: default:
ASSERT(false); UNREACHABLE();
}
} }
return increment;
}
int ForLoopUnroll::evaluateIntConstant(TIntermConstantUnion* node)
{
ASSERT((node != NULL) && (node->getUnionArrayPointer() != NULL));
return node->getIConst(0);
} }
...@@ -7,46 +7,44 @@ ...@@ -7,46 +7,44 @@
#ifndef COMPILER_FORLOOPUNROLL_H_ #ifndef COMPILER_FORLOOPUNROLL_H_
#define COMPILER_FORLOOPUNROLL_H_ #define COMPILER_FORLOOPUNROLL_H_
#include "compiler/translator/intermediate.h" #include "compiler/translator/LoopInfo.h"
struct TLoopIndexInfo { // This class detects for-loops that needs to be unrolled.
int id; // Currently we support two unroll conditions:
int initValue; // 1) kForLoopWithIntegerIndex: unroll if the index type is integer.
int stopValue; // 2) kForLoopWithSamplerArrayIndex: unroll where a sampler array index
int incrementValue; // is also the loop integer index, and reject and fail a compile
TOperator op; // where a sampler array index is also the loop float index.
int currentValue; class ForLoopUnrollMarker : public TIntermTraverser
}; {
public:
class ForLoopUnroll { enum UnrollCondition
public: {
ForLoopUnroll() { } kIntegerIndex,
kSamplerArrayIndex
void FillLoopIndexInfo(TIntermLoop* node, TLoopIndexInfo& info); };
// Update the info.currentValue for the next loop iteration. ForLoopUnrollMarker(UnrollCondition condition)
void Step(); : mUnrollCondition(condition),
mSamplerArrayIndexIsFloatLoopIndex(false),
// Return false if loop condition is no longer satisfied. mVisitSamplerArrayIndexNodeInsideLoop(false)
bool SatisfiesLoopCondition(); {
}
// Check if the symbol is the index of a loop that's unrolled.
bool NeedsToReplaceSymbolWithValue(TIntermSymbol* symbol); virtual bool visitBinary(Visit, TIntermBinary *node);
virtual bool visitLoop(Visit, TIntermLoop *node);
// Return the current value of a given loop index symbol. virtual void visitSymbol(TIntermSymbol *node);
int GetLoopIndexValue(TIntermSymbol* symbol);
bool samplerArrayIndexIsFloatLoopIndex() const
void Push(TLoopIndexInfo& info); {
void Pop(); return mSamplerArrayIndexIsFloatLoopIndex;
}
static void MarkForLoopsWithIntegerIndicesForUnrolling(TIntermNode* root);
private:
private: UnrollCondition mUnrollCondition;
int getLoopIncrement(TIntermLoop* node); TLoopStack mLoopStack;
bool mSamplerArrayIndexIsFloatLoopIndex;
int evaluateIntConstant(TIntermConstantUnion* node); bool mVisitSamplerArrayIndexNodeInsideLoop;
TVector<TLoopIndexInfo> mLoopIndexStack;
}; };
#endif #endif
//
// 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();
}
//
// 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, ...@@ -161,8 +161,8 @@ const ConstantUnion* TOutputGLSLBase::writeConstantUnion(const TType& type,
void TOutputGLSLBase::visitSymbol(TIntermSymbol* node) void TOutputGLSLBase::visitSymbol(TIntermSymbol* node)
{ {
TInfoSinkBase& out = objSink(); TInfoSinkBase& out = objSink();
if (mLoopUnroll.NeedsToReplaceSymbolWithValue(node)) if (mLoopUnrollStack.needsToReplaceSymbolWithValue(node))
out << mLoopUnroll.GetLoopIndexValue(node); out << mLoopUnrollStack.getLoopIndexValue(node);
else else
out << hashVariableName(node->getSymbol()); out << hashVariableName(node->getSymbol());
...@@ -645,7 +645,8 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node) ...@@ -645,7 +645,8 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
TLoopType loopType = node->getType(); TLoopType loopType = node->getType();
if (loopType == ELoopFor) // for loop if (loopType == ELoopFor) // for loop
{ {
if (!node->getUnrollFlag()) { if (!node->getUnrollFlag())
{
out << "for ("; out << "for (";
if (node->getInit()) if (node->getInit())
node->getInit()->traverse(this); node->getInit()->traverse(this);
...@@ -659,6 +660,18 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node) ...@@ -659,6 +660,18 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
node->getExpression()->traverse(this); node->getExpression()->traverse(this);
out << ")\n"; 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 else if (loopType == ELoopWhile) // while loop
{ {
...@@ -676,15 +689,15 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node) ...@@ -676,15 +689,15 @@ bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop* node)
// Loop body. // Loop body.
if (node->getUnrollFlag()) if (node->getUnrollFlag())
{ {
TLoopIndexInfo indexInfo; out << "{\n";
mLoopUnroll.FillLoopIndexInfo(node, indexInfo); mLoopUnrollStack.push(node);
mLoopUnroll.Push(indexInfo); while (mLoopUnrollStack.satisfiesLoopCondition())
while (mLoopUnroll.SatisfiesLoopCondition())
{ {
visitCodeBlock(node->getBody()); visitCodeBlock(node->getBody());
mLoopUnroll.Step(); mLoopUnrollStack.step();
} }
mLoopUnroll.Pop(); mLoopUnrollStack.pop();
out << "}\n";
} }
else else
{ {
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
#include <set> #include <set>
#include "compiler/translator/ForLoopUnroll.h"
#include "compiler/translator/intermediate.h" #include "compiler/translator/intermediate.h"
#include "compiler/translator/LoopInfo.h"
#include "compiler/translator/ParseContext.h" #include "compiler/translator/ParseContext.h"
class TOutputGLSLBase : public TIntermTraverser class TOutputGLSLBase : public TIntermTraverser
...@@ -65,7 +65,8 @@ private: ...@@ -65,7 +65,8 @@ private:
typedef std::set<TString> DeclaredStructs; typedef std::set<TString> DeclaredStructs;
DeclaredStructs mDeclaredStructs; DeclaredStructs mDeclaredStructs;
ForLoopUnroll mLoopUnroll; // Stack of loops that need to be unrolled.
TLoopStack mLoopUnrollStack;
ShArrayIndexClampingStrategy mClampingStrategy; ShArrayIndexClampingStrategy mClampingStrategy;
......
...@@ -6,53 +6,50 @@ ...@@ -6,53 +6,50 @@
#include "GLSLANG/ShaderLang.h" #include "GLSLANG/ShaderLang.h"
#include "compiler/translator/intermediate.h" #include "compiler/translator/intermediate.h"
#include "compiler/translator/LoopInfo.h"
class TInfoSinkBase; 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 // Traverses intermediate tree to ensure that the shader does not exceed the
// minimum functionality mandated in GLSL 1.0 spec, Appendix A. // minimum functionality mandated in GLSL 1.0 spec, Appendix A.
class ValidateLimitations : public TIntermTraverser { class ValidateLimitations : public TIntermTraverser
public: {
ValidateLimitations(ShShaderType shaderType, TInfoSinkBase& sink); public:
ValidateLimitations(ShShaderType shaderType, TInfoSinkBase &sink);
int numErrors() const { return mNumErrors; } int numErrors() const { return mNumErrors; }
virtual bool visitBinary(Visit, TIntermBinary*); virtual bool visitBinary(Visit, TIntermBinary *);
virtual bool visitUnary(Visit, TIntermUnary*); virtual bool visitUnary(Visit, TIntermUnary *);
virtual bool visitAggregate(Visit, TIntermAggregate*); virtual bool visitAggregate(Visit, TIntermAggregate *);
virtual bool visitLoop(Visit, TIntermLoop*); virtual bool visitLoop(Visit, TIntermLoop *);
private: private:
void error(TSourceLoc loc, const char *reason, const char* token); void error(TSourceLoc loc, const char *reason, const char *token);
bool withinLoopBody() const; bool withinLoopBody() const;
bool isLoopIndex(const TIntermSymbol* symbol) const; bool isLoopIndex(TIntermSymbol *symbol);
bool validateLoopType(TIntermLoop* node); bool validateLoopType(TIntermLoop *node);
bool validateForLoopHeader(TIntermLoop* node, TLoopInfo* info);
bool validateForLoopInit(TIntermLoop* node, TLoopInfo* info); bool validateForLoopHeader(TIntermLoop *node);
bool validateForLoopCond(TIntermLoop* node, TLoopInfo* info); // If valid, return the index symbol id; Otherwise, return -1.
bool validateForLoopExpr(TIntermLoop* node, TLoopInfo* info); 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 // Returns true if none of the loop indices is used as the argument to
// the given function out or inout parameter. // the given function out or inout parameter.
bool validateFunctionCall(TIntermAggregate* node); bool validateFunctionCall(TIntermAggregate *node);
bool validateOperation(TIntermOperator* node, TIntermNode* operand); bool validateOperation(TIntermOperator *node, TIntermNode *operand);
// Returns true if indexing does not exceed the minimum functionality // Returns true if indexing does not exceed the minimum functionality
// mandated in GLSL 1.0 spec, Appendix A, Section 5. // mandated in GLSL 1.0 spec, Appendix A, Section 5.
bool isConstExpr(TIntermNode* node); bool isConstExpr(TIntermNode *node);
bool isConstIndexExpr(TIntermNode* node); bool isConstIndexExpr(TIntermNode *node);
bool validateIndexing(TIntermBinary* node); bool validateIndexing(TIntermBinary *node);
ShShaderType mShaderType; ShShaderType mShaderType;
TInfoSinkBase& mSink; TInfoSinkBase &mSink;
int mNumErrors; int mNumErrors;
TLoopStack mLoopStack; TLoopStack mLoopStack;
}; };
......
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