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