// // Copyright (c) 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 #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 OutputVector; OutputVector mOutputs; OutputVector mUnspecifiedLocationOutputs; OutputVector mYuvOutputs; std::set 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 || qualifier == EvqFragDepthEXT) { 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(type.isArray() ? type.getOutermostArraySize() : 1u); const size_t location = static_cast(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(); 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