ValidateOutputs.cpp (6165B)
1 // 2 // Copyright 2013 The ANGLE Project Authors. All rights reserved. 3 // Use of this source code is governed by a BSD-style license that can be 4 // found in the LICENSE file. 5 // 6 // ValidateOutputs validates fragment shader outputs. It checks for conflicting locations, 7 // out-of-range locations, that locations are specified when using multiple outputs, and YUV output 8 // validity. 9 10 #include "compiler/translator/ValidateOutputs.h" 11 12 #include <set> 13 14 #include "compiler/translator/InfoSink.h" 15 #include "compiler/translator/ParseContext.h" 16 #include "compiler/translator/tree_util/IntermTraverse.h" 17 18 namespace sh 19 { 20 21 namespace 22 { 23 24 void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics) 25 { 26 diagnostics->error(symbol.getLine(), reason, symbol.getName().data()); 27 } 28 29 class ValidateOutputsTraverser : public TIntermTraverser 30 { 31 public: 32 ValidateOutputsTraverser(const TExtensionBehavior &extBehavior, int maxDrawBuffers); 33 34 void validate(TDiagnostics *diagnostics) const; 35 36 void visitSymbol(TIntermSymbol *) override; 37 38 private: 39 int mMaxDrawBuffers; 40 bool mAllowUnspecifiedOutputLocationResolution; 41 bool mUsesFragDepth; 42 43 typedef std::vector<TIntermSymbol *> OutputVector; 44 OutputVector mOutputs; 45 OutputVector mUnspecifiedLocationOutputs; 46 OutputVector mYuvOutputs; 47 std::set<int> mVisitedSymbols; // Visited symbol ids. 48 }; 49 50 ValidateOutputsTraverser::ValidateOutputsTraverser(const TExtensionBehavior &extBehavior, 51 int maxDrawBuffers) 52 : TIntermTraverser(true, false, false), 53 mMaxDrawBuffers(maxDrawBuffers), 54 mAllowUnspecifiedOutputLocationResolution( 55 IsExtensionEnabled(extBehavior, TExtension::EXT_blend_func_extended)), 56 mUsesFragDepth(false) 57 {} 58 59 void ValidateOutputsTraverser::visitSymbol(TIntermSymbol *symbol) 60 { 61 if (symbol->variable().symbolType() == SymbolType::Empty) 62 return; 63 64 if (mVisitedSymbols.count(symbol->uniqueId().get()) == 1) 65 return; 66 67 mVisitedSymbols.insert(symbol->uniqueId().get()); 68 69 TQualifier qualifier = symbol->getQualifier(); 70 if (qualifier == EvqFragmentOut) 71 { 72 if (symbol->getType().getLayoutQualifier().location != -1) 73 { 74 mOutputs.push_back(symbol); 75 } 76 else if (symbol->getType().getLayoutQualifier().yuv == true) 77 { 78 mYuvOutputs.push_back(symbol); 79 } 80 else 81 { 82 mUnspecifiedLocationOutputs.push_back(symbol); 83 } 84 } 85 else if (qualifier == EvqFragDepth) 86 { 87 mUsesFragDepth = true; 88 } 89 } 90 91 void ValidateOutputsTraverser::validate(TDiagnostics *diagnostics) const 92 { 93 ASSERT(diagnostics); 94 OutputVector validOutputs(mMaxDrawBuffers, nullptr); 95 OutputVector validSecondaryOutputs(mMaxDrawBuffers, nullptr); 96 97 for (const auto &symbol : mOutputs) 98 { 99 const TType &type = symbol->getType(); 100 ASSERT(!type.isArrayOfArrays()); // Disallowed in GLSL ES 3.10 section 4.3.6. 101 const size_t elementCount = 102 static_cast<size_t>(type.isArray() ? type.getOutermostArraySize() : 1u); 103 const size_t location = static_cast<size_t>(type.getLayoutQualifier().location); 104 105 ASSERT(type.getLayoutQualifier().location != -1); 106 107 OutputVector *validOutputsToUse = &validOutputs; 108 // The default index is 0, so we only assign the output to secondary outputs in case the 109 // index is explicitly set to 1. 110 if (type.getLayoutQualifier().index == 1) 111 { 112 validOutputsToUse = &validSecondaryOutputs; 113 } 114 115 if (location + elementCount <= validOutputsToUse->size()) 116 { 117 for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++) 118 { 119 const size_t offsetLocation = location + elementIndex; 120 if ((*validOutputsToUse)[offsetLocation]) 121 { 122 std::stringstream strstr = sh::InitializeStream<std::stringstream>(); 123 strstr << "conflicting output locations with previously defined output '" 124 << (*validOutputsToUse)[offsetLocation]->getName() << "'"; 125 error(*symbol, strstr.str().c_str(), diagnostics); 126 } 127 else 128 { 129 (*validOutputsToUse)[offsetLocation] = symbol; 130 } 131 } 132 } 133 else 134 { 135 if (elementCount > 0) 136 { 137 error(*symbol, 138 elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS" 139 : "output location must be < MAX_DRAW_BUFFERS", 140 diagnostics); 141 } 142 } 143 } 144 145 if (!mAllowUnspecifiedOutputLocationResolution && 146 ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) || 147 mUnspecifiedLocationOutputs.size() > 1)) 148 { 149 for (const auto &symbol : mUnspecifiedLocationOutputs) 150 { 151 error(*symbol, 152 "must explicitly specify all locations when using multiple fragment outputs", 153 diagnostics); 154 } 155 } 156 157 if (!mYuvOutputs.empty() && (mYuvOutputs.size() > 1 || mUsesFragDepth || !mOutputs.empty() || 158 !mUnspecifiedLocationOutputs.empty())) 159 { 160 for (const auto &symbol : mYuvOutputs) 161 { 162 error(*symbol, 163 "not allowed to specify yuv qualifier when using depth or multiple color " 164 "fragment outputs", 165 diagnostics); 166 } 167 } 168 } 169 170 } // anonymous namespace 171 172 bool ValidateOutputs(TIntermBlock *root, 173 const TExtensionBehavior &extBehavior, 174 int maxDrawBuffers, 175 TDiagnostics *diagnostics) 176 { 177 ValidateOutputsTraverser validateOutputs(extBehavior, maxDrawBuffers); 178 root->traverse(&validateOutputs); 179 int numErrorsBefore = diagnostics->numErrors(); 180 validateOutputs.validate(diagnostics); 181 return (diagnostics->numErrors() == numErrorsBefore); 182 } 183 184 } // namespace sh