diff options
Diffstat (limited to 'gfx/angle/checkout/src/compiler/translator/ValidateOutputs.cpp')
-rw-r--r-- | gfx/angle/checkout/src/compiler/translator/ValidateOutputs.cpp | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/gfx/angle/checkout/src/compiler/translator/ValidateOutputs.cpp b/gfx/angle/checkout/src/compiler/translator/ValidateOutputs.cpp new file mode 100644 index 0000000000..5ba70c4de1 --- /dev/null +++ b/gfx/angle/checkout/src/compiler/translator/ValidateOutputs.cpp @@ -0,0 +1,184 @@ +// +// Copyright 2013 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. +// +// ValidateOutputs validates fragment shader outputs. It checks for conflicting locations, +// out-of-range locations, that locations are specified when using multiple outputs, and YUV output +// validity. + +#include "compiler/translator/ValidateOutputs.h" + +#include <set> + +#include "compiler/translator/InfoSink.h" +#include "compiler/translator/ParseContext.h" +#include "compiler/translator/tree_util/IntermTraverse.h" + +namespace sh +{ + +namespace +{ + +void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics) +{ + diagnostics->error(symbol.getLine(), reason, symbol.getName().data()); +} + +class ValidateOutputsTraverser : public TIntermTraverser +{ + public: + ValidateOutputsTraverser(const TExtensionBehavior &extBehavior, int maxDrawBuffers); + + void validate(TDiagnostics *diagnostics) const; + + void visitSymbol(TIntermSymbol *) override; + + private: + int mMaxDrawBuffers; + bool mAllowUnspecifiedOutputLocationResolution; + bool mUsesFragDepth; + + typedef std::vector<TIntermSymbol *> OutputVector; + OutputVector mOutputs; + OutputVector mUnspecifiedLocationOutputs; + OutputVector mYuvOutputs; + std::set<int> mVisitedSymbols; // Visited symbol ids. +}; + +ValidateOutputsTraverser::ValidateOutputsTraverser(const TExtensionBehavior &extBehavior, + int maxDrawBuffers) + : TIntermTraverser(true, false, false), + mMaxDrawBuffers(maxDrawBuffers), + mAllowUnspecifiedOutputLocationResolution( + IsExtensionEnabled(extBehavior, TExtension::EXT_blend_func_extended)), + mUsesFragDepth(false) +{} + +void ValidateOutputsTraverser::visitSymbol(TIntermSymbol *symbol) +{ + if (symbol->variable().symbolType() == SymbolType::Empty) + return; + + if (mVisitedSymbols.count(symbol->uniqueId().get()) == 1) + return; + + mVisitedSymbols.insert(symbol->uniqueId().get()); + + TQualifier qualifier = symbol->getQualifier(); + if (qualifier == EvqFragmentOut) + { + if (symbol->getType().getLayoutQualifier().location != -1) + { + mOutputs.push_back(symbol); + } + else if (symbol->getType().getLayoutQualifier().yuv == true) + { + mYuvOutputs.push_back(symbol); + } + else + { + mUnspecifiedLocationOutputs.push_back(symbol); + } + } + else if (qualifier == EvqFragDepth) + { + mUsesFragDepth = true; + } +} + +void ValidateOutputsTraverser::validate(TDiagnostics *diagnostics) const +{ + ASSERT(diagnostics); + OutputVector validOutputs(mMaxDrawBuffers, nullptr); + OutputVector validSecondaryOutputs(mMaxDrawBuffers, nullptr); + + for (const auto &symbol : mOutputs) + { + const TType &type = symbol->getType(); + ASSERT(!type.isArrayOfArrays()); // Disallowed in GLSL ES 3.10 section 4.3.6. + const size_t elementCount = + static_cast<size_t>(type.isArray() ? type.getOutermostArraySize() : 1u); + const size_t location = static_cast<size_t>(type.getLayoutQualifier().location); + + ASSERT(type.getLayoutQualifier().location != -1); + + OutputVector *validOutputsToUse = &validOutputs; + // The default index is 0, so we only assign the output to secondary outputs in case the + // index is explicitly set to 1. + if (type.getLayoutQualifier().index == 1) + { + validOutputsToUse = &validSecondaryOutputs; + } + + if (location + elementCount <= validOutputsToUse->size()) + { + for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++) + { + const size_t offsetLocation = location + elementIndex; + if ((*validOutputsToUse)[offsetLocation]) + { + std::stringstream strstr = sh::InitializeStream<std::stringstream>(); + strstr << "conflicting output locations with previously defined output '" + << (*validOutputsToUse)[offsetLocation]->getName() << "'"; + error(*symbol, strstr.str().c_str(), diagnostics); + } + else + { + (*validOutputsToUse)[offsetLocation] = symbol; + } + } + } + else + { + if (elementCount > 0) + { + error(*symbol, + elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS" + : "output location must be < MAX_DRAW_BUFFERS", + diagnostics); + } + } + } + + if (!mAllowUnspecifiedOutputLocationResolution && + ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) || + mUnspecifiedLocationOutputs.size() > 1)) + { + for (const auto &symbol : mUnspecifiedLocationOutputs) + { + error(*symbol, + "must explicitly specify all locations when using multiple fragment outputs", + diagnostics); + } + } + + if (!mYuvOutputs.empty() && (mYuvOutputs.size() > 1 || mUsesFragDepth || !mOutputs.empty() || + !mUnspecifiedLocationOutputs.empty())) + { + for (const auto &symbol : mYuvOutputs) + { + error(*symbol, + "not allowed to specify yuv qualifier when using depth or multiple color " + "fragment outputs", + diagnostics); + } + } +} + +} // anonymous namespace + +bool ValidateOutputs(TIntermBlock *root, + const TExtensionBehavior &extBehavior, + int maxDrawBuffers, + TDiagnostics *diagnostics) +{ + ValidateOutputsTraverser validateOutputs(extBehavior, maxDrawBuffers); + root->traverse(&validateOutputs); + int numErrorsBefore = diagnostics->numErrors(); + validateOutputs.validate(diagnostics); + return (diagnostics->numErrors() == numErrorsBefore); +} + +} // namespace sh |