| // |
| // Copyright 2020 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. |
| // |
| // The ValidateClipCullDistance function checks if the sum of array sizes for gl_ClipDistance and |
| // gl_CullDistance exceeds gl_MaxCombinedClipAndCullDistances |
| // |
| |
| #include "ValidateClipCullDistance.h" |
| |
| #include "compiler/translator/Diagnostics.h" |
| #include "compiler/translator/SymbolTable.h" |
| #include "compiler/translator/tree_util/IntermTraverse.h" |
| #include "compiler/translator/util.h" |
| |
| namespace sh |
| { |
| |
| namespace |
| { |
| |
| void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics) |
| { |
| diagnostics->error(symbol.getLine(), reason, symbol.getName().data()); |
| } |
| |
| class ValidateClipCullDistanceTraverser : public TIntermTraverser |
| { |
| public: |
| ValidateClipCullDistanceTraverser(); |
| void validate(TDiagnostics *diagnostics, const unsigned int maxCombinedClipAndCullDistances); |
| |
| private: |
| bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; |
| bool visitBinary(Visit visit, TIntermBinary *node) override; |
| |
| unsigned int mClipDistanceSize; |
| unsigned int mCullDistanceSize; |
| |
| unsigned int mMaxClipDistanceIndex; |
| unsigned int mMaxCullDistanceIndex; |
| |
| const TIntermSymbol *mClipDistance; |
| const TIntermSymbol *mCullDistance; |
| }; |
| |
| ValidateClipCullDistanceTraverser::ValidateClipCullDistanceTraverser() |
| : TIntermTraverser(true, false, false), |
| mClipDistanceSize(0), |
| mCullDistanceSize(0), |
| mMaxClipDistanceIndex(0), |
| mMaxCullDistanceIndex(0), |
| mClipDistance(nullptr), |
| mCullDistance(nullptr) |
| {} |
| |
| bool ValidateClipCullDistanceTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node) |
| { |
| const TIntermSequence &sequence = *(node->getSequence()); |
| |
| if (sequence.size() != 1) |
| { |
| return true; |
| } |
| |
| const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode(); |
| if (symbol == nullptr) |
| { |
| return true; |
| } |
| |
| if (symbol->getName() == "gl_ClipDistance") |
| { |
| mClipDistanceSize = symbol->getOutermostArraySize(); |
| mClipDistance = symbol; |
| } |
| else if (symbol->getName() == "gl_CullDistance") |
| { |
| mCullDistanceSize = symbol->getOutermostArraySize(); |
| mCullDistance = symbol; |
| } |
| |
| return true; |
| } |
| |
| bool ValidateClipCullDistanceTraverser::visitBinary(Visit visit, TIntermBinary *node) |
| { |
| TOperator op = node->getOp(); |
| if (op != EOpIndexDirect && op != EOpIndexIndirect) |
| { |
| return true; |
| } |
| |
| TIntermSymbol *left = node->getLeft()->getAsSymbolNode(); |
| if (!left) |
| { |
| return true; |
| } |
| |
| ImmutableString varName(left->getName()); |
| if (varName != "gl_ClipDistance" && varName != "gl_CullDistance") |
| { |
| return true; |
| } |
| |
| const TConstantUnion *constIdx = node->getRight()->getConstantValue(); |
| if (constIdx) |
| { |
| unsigned int idx = 0; |
| switch (constIdx->getType()) |
| { |
| case EbtInt: |
| idx = constIdx->getIConst(); |
| break; |
| case EbtUInt: |
| idx = constIdx->getUConst(); |
| break; |
| case EbtFloat: |
| idx = static_cast<unsigned int>(constIdx->getFConst()); |
| break; |
| case EbtBool: |
| idx = constIdx->getBConst() ? 1 : 0; |
| break; |
| default: |
| UNREACHABLE(); |
| break; |
| } |
| |
| if (varName == "gl_ClipDistance") |
| { |
| if (idx > mMaxClipDistanceIndex) |
| { |
| mMaxClipDistanceIndex = idx; |
| if (!mClipDistance) |
| { |
| mClipDistance = left; |
| } |
| } |
| } |
| else |
| { |
| ASSERT(varName == "gl_CullDistance"); |
| if (idx > mMaxCullDistanceIndex) |
| { |
| mMaxCullDistanceIndex = idx; |
| if (!mCullDistance) |
| { |
| mCullDistance = left; |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| void ValidateClipCullDistanceTraverser::validate(TDiagnostics *diagnostics, |
| const unsigned int maxCombinedClipAndCullDistances) |
| { |
| ASSERT(diagnostics); |
| |
| unsigned int enabledClipDistances = |
| (mClipDistanceSize > 0 ? mClipDistanceSize |
| : (mClipDistance ? mMaxClipDistanceIndex + 1 : 0)); |
| unsigned int enabledCullDistances = |
| (mCullDistanceSize > 0 ? mCullDistanceSize |
| : (mCullDistance ? mMaxCullDistanceIndex + 1 : 0)); |
| unsigned int combinedClipAndCullDistances = |
| (enabledClipDistances > 0 && enabledCullDistances > 0 |
| ? enabledClipDistances + enabledCullDistances |
| : 0); |
| |
| if (combinedClipAndCullDistances > maxCombinedClipAndCullDistances) |
| { |
| const TIntermSymbol *greaterSymbol = |
| (enabledClipDistances >= enabledCullDistances ? mClipDistance : mCullDistance); |
| |
| std::stringstream strstr = sh::InitializeStream<std::stringstream>(); |
| strstr << "The sum of 'gl_ClipDistance' and 'gl_CullDistance' size is greater than " |
| "gl_MaxCombinedClipAndCullDistances (" |
| << combinedClipAndCullDistances << " > " << maxCombinedClipAndCullDistances << ")"; |
| error(*greaterSymbol, strstr.str().c_str(), diagnostics); |
| } |
| } |
| |
| } // anonymous namespace |
| |
| bool ValidateClipCullDistance(TIntermBlock *root, |
| TDiagnostics *diagnostics, |
| const unsigned int maxCombinedClipAndCullDistances) |
| { |
| ValidateClipCullDistanceTraverser varyingValidator; |
| root->traverse(&varyingValidator); |
| int numErrorsBefore = diagnostics->numErrors(); |
| varyingValidator.validate(diagnostics, maxCombinedClipAndCullDistances); |
| return (diagnostics->numErrors() == numErrorsBefore); |
| } |
| |
| } // namespace sh |