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 {
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 }
59 
visitSymbol(TIntermSymbol * symbol)60 void ValidateOutputsTraverser::visitSymbol(TIntermSymbol *symbol)
61 {
62     if (symbol->variable().symbolType() == SymbolType::Empty)
63         return;
64 
65     if (mVisitedSymbols.count(symbol->uniqueId().get()) == 1)
66         return;
67 
68     mVisitedSymbols.insert(symbol->uniqueId().get());
69 
70     TQualifier qualifier = symbol->getQualifier();
71     if (qualifier == EvqFragmentOut)
72     {
73         if (symbol->getType().getLayoutQualifier().location != -1)
74         {
75             mOutputs.push_back(symbol);
76         }
77         else if (symbol->getType().getLayoutQualifier().yuv == true)
78         {
79             mYuvOutputs.push_back(symbol);
80         }
81         else
82         {
83             mUnspecifiedLocationOutputs.push_back(symbol);
84         }
85     }
86     else if (qualifier == EvqFragDepth || qualifier == EvqFragDepthEXT)
87     {
88         mUsesFragDepth = true;
89     }
90 }
91 
validate(TDiagnostics * diagnostics) const92 void ValidateOutputsTraverser::validate(TDiagnostics *diagnostics) const
93 {
94     ASSERT(diagnostics);
95     OutputVector validOutputs(mMaxDrawBuffers);
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         if (location + elementCount <= validOutputs.size())
108         {
109             for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++)
110             {
111                 const size_t offsetLocation = location + elementIndex;
112                 if (validOutputs[offsetLocation])
113                 {
114                     std::stringstream strstr;
115                     strstr << "conflicting output locations with previously defined output '"
116                            << validOutputs[offsetLocation]->getName() << "'";
117                     error(*symbol, strstr.str().c_str(), diagnostics);
118                 }
119                 else
120                 {
121                     validOutputs[offsetLocation] = symbol;
122                 }
123             }
124         }
125         else
126         {
127             if (elementCount > 0)
128             {
129                 error(*symbol,
130                       elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
131                                        : "output location must be < MAX_DRAW_BUFFERS",
132                       diagnostics);
133             }
134         }
135     }
136 
137     if (!mAllowUnspecifiedOutputLocationResolution &&
138         ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) ||
139          mUnspecifiedLocationOutputs.size() > 1))
140     {
141         for (const auto &symbol : mUnspecifiedLocationOutputs)
142         {
143             error(*symbol,
144                   "must explicitly specify all locations when using multiple fragment outputs",
145                   diagnostics);
146         }
147     }
148 
149     if (!mYuvOutputs.empty() && (mYuvOutputs.size() > 1 || mUsesFragDepth || !mOutputs.empty() ||
150                                  !mUnspecifiedLocationOutputs.empty()))
151     {
152         for (const auto &symbol : mYuvOutputs)
153         {
154             error(*symbol,
155                   "not allowed to specify yuv qualifier when using depth or multiple color "
156                   "fragment outputs",
157                   diagnostics);
158         }
159     }
160 }
161 
162 }  // anonymous namespace
163 
ValidateOutputs(TIntermBlock * root,const TExtensionBehavior & extBehavior,int maxDrawBuffers,TDiagnostics * diagnostics)164 bool ValidateOutputs(TIntermBlock *root,
165                      const TExtensionBehavior &extBehavior,
166                      int maxDrawBuffers,
167                      TDiagnostics *diagnostics)
168 {
169     ValidateOutputsTraverser validateOutputs(extBehavior, maxDrawBuffers);
170     root->traverse(&validateOutputs);
171     int numErrorsBefore = diagnostics->numErrors();
172     validateOutputs.validate(diagnostics);
173     return (diagnostics->numErrors() == numErrorsBefore);
174 }
175 
176 }  // namespace sh
177