1 | // |
2 | // Copyright (c) 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 || qualifier == EvqFragDepthEXT) |
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 |
185 | |