// // 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(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(); 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