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