// // 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 maxCullDistances, const unsigned int maxCombinedClipAndCullDistances, uint8_t *clipDistanceSizeOut, uint8_t *cullDistanceSizeOut, bool *clipDistanceRedeclaredOut, bool *cullDistanceRedeclaredOut, bool *clipDistanceUsedOut); private: bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; bool visitBinary(Visit visit, TIntermBinary *node) override; uint8_t mClipDistanceSize; uint8_t mCullDistanceSize; int8_t mMaxClipDistanceIndex; int8_t mMaxCullDistanceIndex; bool mHasNonConstClipDistanceIndex; bool mHasNonConstCullDistanceIndex; const TIntermSymbol *mClipDistance; const TIntermSymbol *mCullDistance; }; ValidateClipCullDistanceTraverser::ValidateClipCullDistanceTraverser() : TIntermTraverser(true, false, false), mClipDistanceSize(0), mCullDistanceSize(0), mMaxClipDistanceIndex(-1), mMaxCullDistanceIndex(-1), mHasNonConstClipDistanceIndex(false), mHasNonConstCullDistanceIndex(false), 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 = static_cast(symbol->getOutermostArraySize()); mClipDistance = symbol; } else if (symbol->getName() == "gl_CullDistance") { mCullDistanceSize = static_cast(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) { 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 = static_cast(idx); if (!mClipDistance) { mClipDistance = left; } } } else { ASSERT(varName == "gl_CullDistance"); if (idx > mMaxCullDistanceIndex) { mMaxCullDistanceIndex = static_cast(idx); if (!mCullDistance) { mCullDistance = left; } } } } else { if (varName == "gl_ClipDistance") { mHasNonConstClipDistanceIndex = true; if (!mClipDistance) { mClipDistance = left; } } else { ASSERT(varName == "gl_CullDistance"); mHasNonConstCullDistanceIndex = true; if (!mCullDistance) { mCullDistance = left; } } } return true; } void ValidateClipCullDistanceTraverser::validate(TDiagnostics *diagnostics, const unsigned int maxCullDistances, const unsigned int maxCombinedClipAndCullDistances, uint8_t *clipDistanceSizeOut, uint8_t *cullDistanceSizeOut, bool *clipDistanceRedeclaredOut, bool *cullDistanceRedeclaredOut, bool *clipDistanceUsedOut) { ASSERT(diagnostics); if (mClipDistanceSize == 0 && mHasNonConstClipDistanceIndex) { error(*mClipDistance, "The array must be sized by the shader either redeclaring it with a size or " "indexing it only with constant integral expressions", diagnostics); } if (mCullDistanceSize == 0 && mHasNonConstCullDistanceIndex) { error(*mCullDistance, "The array must be sized by the shader either redeclaring it with a size or " "indexing it only with constant integral expressions", 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 (enabledCullDistances > 0 && maxCullDistances == 0) { error(*mCullDistance, "Cull distance functionality is not available", diagnostics); } 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); } // Update the compiler state *clipDistanceSizeOut = mClipDistanceSize ? mClipDistanceSize : (mMaxClipDistanceIndex + 1); *cullDistanceSizeOut = mCullDistanceSize ? mCullDistanceSize : (mMaxCullDistanceIndex + 1); *clipDistanceRedeclaredOut = mClipDistanceSize != 0; *cullDistanceRedeclaredOut = mCullDistanceSize != 0; *clipDistanceUsedOut = (mMaxClipDistanceIndex != -1) || mHasNonConstClipDistanceIndex; } } // anonymous namespace bool ValidateClipCullDistance(TIntermBlock *root, TDiagnostics *diagnostics, const unsigned int maxCullDistances, const unsigned int maxCombinedClipAndCullDistances, uint8_t *clipDistanceSizeOut, uint8_t *cullDistanceSizeOut, bool *clipDistanceRedeclaredOut, bool *cullDistanceRedeclaredOut, bool *clipDistanceUsedOut) { ValidateClipCullDistanceTraverser varyingValidator; root->traverse(&varyingValidator); int numErrorsBefore = diagnostics->numErrors(); varyingValidator.validate(diagnostics, maxCullDistances, maxCombinedClipAndCullDistances, clipDistanceSizeOut, cullDistanceSizeOut, clipDistanceRedeclaredOut, cullDistanceRedeclaredOut, clipDistanceUsedOut); return (diagnostics->numErrors() == numErrorsBefore); } } // namespace sh