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, 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 
ValidateOutputsTraverser(const TExtensionBehavior & extBehavior,int maxDrawBuffers)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 
visitSymbol(TIntermSymbol * symbol)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 
validate(TDiagnostics * diagnostics) const91 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 
ValidateOutputs(TIntermBlock * root,const TExtensionBehavior & extBehavior,int maxDrawBuffers,TDiagnostics * diagnostics)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