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
error(const TIntermSymbol & symbol,const char * reason,TDiagnostics * diagnostics)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,
33 int maxDrawBuffers,
34 bool usesPixelLocalStorage);
35
36 void validate(TDiagnostics *diagnostics) const;
37
38 void visitSymbol(TIntermSymbol *) override;
39
40 private:
41 int mMaxDrawBuffers;
42 bool mEnablesBlendFuncExtended;
43 bool mUsesPixelLocalStorage;
44 bool mUsesFragDepth;
45
46 typedef std::vector<TIntermSymbol *> OutputVector;
47 OutputVector mOutputs;
48 OutputVector mUnspecifiedLocationOutputs;
49 OutputVector mYuvOutputs;
50 std::set<int> mVisitedSymbols; // Visited symbol ids.
51 };
52
ValidateOutputsTraverser(const TExtensionBehavior & extBehavior,int maxDrawBuffers,bool usesPixelLocalStorage)53 ValidateOutputsTraverser::ValidateOutputsTraverser(const TExtensionBehavior &extBehavior,
54 int maxDrawBuffers,
55 bool usesPixelLocalStorage)
56 : TIntermTraverser(true, false, false),
57 mMaxDrawBuffers(maxDrawBuffers),
58 mEnablesBlendFuncExtended(
59 IsExtensionEnabled(extBehavior, TExtension::EXT_blend_func_extended)),
60 mUsesPixelLocalStorage(usesPixelLocalStorage),
61 mUsesFragDepth(false)
62 {}
63
visitSymbol(TIntermSymbol * symbol)64 void ValidateOutputsTraverser::visitSymbol(TIntermSymbol *symbol)
65 {
66 if (symbol->variable().symbolType() == SymbolType::Empty)
67 return;
68
69 if (mVisitedSymbols.count(symbol->uniqueId().get()) == 1)
70 return;
71
72 mVisitedSymbols.insert(symbol->uniqueId().get());
73
74 TQualifier qualifier = symbol->getQualifier();
75 if (qualifier == EvqFragmentOut)
76 {
77 if (symbol->getType().getLayoutQualifier().location != -1)
78 {
79 mOutputs.push_back(symbol);
80 }
81 else if (symbol->getType().getLayoutQualifier().yuv == true)
82 {
83 mYuvOutputs.push_back(symbol);
84 }
85 else
86 {
87 mUnspecifiedLocationOutputs.push_back(symbol);
88 }
89 }
90 else if (qualifier == EvqFragDepth)
91 {
92 mUsesFragDepth = true;
93 }
94 }
95
validate(TDiagnostics * diagnostics) const96 void ValidateOutputsTraverser::validate(TDiagnostics *diagnostics) const
97 {
98 ASSERT(diagnostics);
99 OutputVector validOutputs(mMaxDrawBuffers, nullptr);
100 OutputVector validSecondaryOutputs(mMaxDrawBuffers, nullptr);
101
102 for (const auto &symbol : mOutputs)
103 {
104 const TType &type = symbol->getType();
105 ASSERT(!type.isArrayOfArrays()); // Disallowed in GLSL ES 3.10 section 4.3.6.
106 const size_t elementCount =
107 static_cast<size_t>(type.isArray() ? type.getOutermostArraySize() : 1u);
108 const size_t location = static_cast<size_t>(type.getLayoutQualifier().location);
109
110 ASSERT(type.getLayoutQualifier().location != -1);
111
112 OutputVector *validOutputsToUse = &validOutputs;
113 // The default index is 0, so we only assign the output to secondary outputs in case the
114 // index is explicitly set to 1.
115 if (type.getLayoutQualifier().index == 1)
116 {
117 validOutputsToUse = &validSecondaryOutputs;
118 }
119
120 if (location + elementCount <= validOutputsToUse->size())
121 {
122 for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++)
123 {
124 const size_t offsetLocation = location + elementIndex;
125 if ((*validOutputsToUse)[offsetLocation])
126 {
127 std::stringstream strstr = sh::InitializeStream<std::stringstream>();
128 strstr << "conflicting output locations with previously defined output '"
129 << (*validOutputsToUse)[offsetLocation]->getName() << "'";
130 error(*symbol, strstr.str().c_str(), diagnostics);
131 }
132 else
133 {
134 (*validOutputsToUse)[offsetLocation] = symbol;
135 }
136 }
137 }
138 else
139 {
140 if (elementCount > 0)
141 {
142 error(*symbol,
143 elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
144 : "output location must be < MAX_DRAW_BUFFERS",
145 diagnostics);
146 }
147 }
148 }
149
150 if ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) ||
151 mUnspecifiedLocationOutputs.size() > 1)
152 {
153 const char *unspecifiedLocationErrorMessage = nullptr;
154 if (!mEnablesBlendFuncExtended)
155 {
156 unspecifiedLocationErrorMessage =
157 "must explicitly specify all locations when using multiple fragment outputs";
158 }
159 else if (mUsesPixelLocalStorage)
160 {
161 unspecifiedLocationErrorMessage =
162 "must explicitly specify all locations when using multiple fragment outputs and "
163 "pixel local storage, even if EXT_blend_func_extended is enabled";
164 }
165 if (unspecifiedLocationErrorMessage != nullptr)
166 {
167 for (const auto &symbol : mUnspecifiedLocationOutputs)
168 {
169 error(*symbol, unspecifiedLocationErrorMessage, diagnostics);
170 }
171 }
172 }
173
174 if (!mYuvOutputs.empty() && (mYuvOutputs.size() > 1 || mUsesFragDepth || !mOutputs.empty() ||
175 !mUnspecifiedLocationOutputs.empty()))
176 {
177 for (const auto &symbol : mYuvOutputs)
178 {
179 error(*symbol,
180 "not allowed to specify yuv qualifier when using depth or multiple color "
181 "fragment outputs",
182 diagnostics);
183 }
184 }
185 }
186
187 } // anonymous namespace
188
ValidateOutputs(TIntermBlock * root,const TExtensionBehavior & extBehavior,int maxDrawBuffers,bool usesPixelLocalStorage,TDiagnostics * diagnostics)189 bool ValidateOutputs(TIntermBlock *root,
190 const TExtensionBehavior &extBehavior,
191 int maxDrawBuffers,
192 bool usesPixelLocalStorage,
193 TDiagnostics *diagnostics)
194 {
195 ValidateOutputsTraverser validateOutputs(extBehavior, maxDrawBuffers, usesPixelLocalStorage);
196 root->traverse(&validateOutputs);
197 int numErrorsBefore = diagnostics->numErrors();
198 validateOutputs.validate(diagnostics);
199 return (diagnostics->numErrors() == numErrorsBefore);
200 }
201
202 } // namespace sh
203