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/IntermTraverse.h"
16 #include "compiler/translator/ParseContext.h"
17
18 namespace sh
19 {
20
21 namespace
22 {
error(const TIntermSymbol & symbol,const char * reason,TDiagnostics * diagnostics)23 void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
24 {
25 diagnostics->error(symbol.getLine(), reason, symbol.getSymbol().c_str());
26 }
27
28 class ValidateOutputsTraverser : public TIntermTraverser
29 {
30 public:
31 ValidateOutputsTraverser(const TExtensionBehavior &extBehavior, int maxDrawBuffers);
32
33 void validate(TDiagnostics *diagnostics) const;
34
35 void visitSymbol(TIntermSymbol *) override;
36
37 private:
38 int mMaxDrawBuffers;
39 bool mAllowUnspecifiedOutputLocationResolution;
40 bool mUsesFragDepth;
41
42 typedef std::vector<TIntermSymbol *> OutputVector;
43 OutputVector mOutputs;
44 OutputVector mUnspecifiedLocationOutputs;
45 OutputVector mYuvOutputs;
46 std::set<std::string> mVisitedSymbols;
47 };
48
ValidateOutputsTraverser(const TExtensionBehavior & extBehavior,int maxDrawBuffers)49 ValidateOutputsTraverser::ValidateOutputsTraverser(const TExtensionBehavior &extBehavior,
50 int maxDrawBuffers)
51 : TIntermTraverser(true, false, false),
52 mMaxDrawBuffers(maxDrawBuffers),
53 mAllowUnspecifiedOutputLocationResolution(
54 IsExtensionEnabled(extBehavior, TExtension::EXT_blend_func_extended)),
55 mUsesFragDepth(false)
56 {
57 }
58
visitSymbol(TIntermSymbol * symbol)59 void ValidateOutputsTraverser::visitSymbol(TIntermSymbol *symbol)
60 {
61 TString name = symbol->getSymbol();
62 TQualifier qualifier = symbol->getQualifier();
63
64 if (mVisitedSymbols.count(name.c_str()) == 1)
65 return;
66
67 mVisitedSymbols.insert(name.c_str());
68
69 if (qualifier == EvqFragmentOut)
70 {
71 if (symbol->getType().getLayoutQualifier().location != -1)
72 {
73 mOutputs.push_back(symbol);
74 }
75 else if (symbol->getType().getLayoutQualifier().yuv == true)
76 {
77 mYuvOutputs.push_back(symbol);
78 }
79 else
80 {
81 mUnspecifiedLocationOutputs.push_back(symbol);
82 }
83 }
84 else if (qualifier == EvqFragDepth || qualifier == EvqFragDepthEXT)
85 {
86 mUsesFragDepth = true;
87 }
88 }
89
validate(TDiagnostics * diagnostics) const90 void ValidateOutputsTraverser::validate(TDiagnostics *diagnostics) const
91 {
92 ASSERT(diagnostics);
93 OutputVector validOutputs(mMaxDrawBuffers);
94
95 for (const auto &symbol : mOutputs)
96 {
97 const TType &type = symbol->getType();
98 ASSERT(!type.isArrayOfArrays()); // Disallowed in GLSL ES 3.10 section 4.3.6.
99 const size_t elementCount =
100 static_cast<size_t>(type.isArray() ? type.getOutermostArraySize() : 1u);
101 const size_t location = static_cast<size_t>(type.getLayoutQualifier().location);
102
103 ASSERT(type.getLayoutQualifier().location != -1);
104
105 if (location + elementCount <= validOutputs.size())
106 {
107 for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++)
108 {
109 const size_t offsetLocation = location + elementIndex;
110 if (validOutputs[offsetLocation])
111 {
112 std::stringstream strstr;
113 strstr << "conflicting output locations with previously defined output '"
114 << validOutputs[offsetLocation]->getSymbol() << "'";
115 error(*symbol, strstr.str().c_str(), diagnostics);
116 }
117 else
118 {
119 validOutputs[offsetLocation] = symbol;
120 }
121 }
122 }
123 else
124 {
125 if (elementCount > 0)
126 {
127 error(*symbol,
128 elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
129 : "output location must be < MAX_DRAW_BUFFERS",
130 diagnostics);
131 }
132 }
133 }
134
135 if (!mAllowUnspecifiedOutputLocationResolution &&
136 ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) ||
137 mUnspecifiedLocationOutputs.size() > 1))
138 {
139 for (const auto &symbol : mUnspecifiedLocationOutputs)
140 {
141 error(*symbol,
142 "must explicitly specify all locations when using multiple fragment outputs",
143 diagnostics);
144 }
145 }
146
147 if (!mYuvOutputs.empty() && (mYuvOutputs.size() > 1 || mUsesFragDepth || !mOutputs.empty() ||
148 !mUnspecifiedLocationOutputs.empty()))
149 {
150 for (const auto &symbol : mYuvOutputs)
151 {
152 error(*symbol,
153 "not allowed to specify yuv qualifier when using depth or multiple color "
154 "fragment outputs",
155 diagnostics);
156 }
157 }
158 }
159
160 } // anonymous namespace
161
ValidateOutputs(TIntermBlock * root,const TExtensionBehavior & extBehavior,int maxDrawBuffers,TDiagnostics * diagnostics)162 bool ValidateOutputs(TIntermBlock *root,
163 const TExtensionBehavior &extBehavior,
164 int maxDrawBuffers,
165 TDiagnostics *diagnostics)
166 {
167 ValidateOutputsTraverser validateOutputs(extBehavior, maxDrawBuffers);
168 root->traverse(&validateOutputs);
169 int numErrorsBefore = diagnostics->numErrors();
170 validateOutputs.validate(diagnostics);
171 return (diagnostics->numErrors() == numErrorsBefore);
172 }
173
174 } // namespace sh
175