• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,
33                              int maxDrawBuffers,
34                              bool usesPixelLocalStorage);
35 
36     void validate(TDiagnostics *diagnostics) const;
37 
38     void visitSymbol(TIntermSymbol *) override;
39 
40   private:
41     int mMaxDrawBuffers;
42     bool mEnablesBlendFuncExtended;
43     bool mUsesPixelLocalStorage;
44     bool mUsesFragDepth;
45 
46     typedef std::vector<TIntermSymbol *> OutputVector;
47     OutputVector mOutputs;
48     OutputVector mUnspecifiedLocationOutputs;
49     OutputVector mYuvOutputs;
50     std::set<int> mVisitedSymbols;  // Visited symbol ids.
51 };
52 
ValidateOutputsTraverser(const TExtensionBehavior & extBehavior,int maxDrawBuffers,bool usesPixelLocalStorage)53 ValidateOutputsTraverser::ValidateOutputsTraverser(const TExtensionBehavior &extBehavior,
54                                                    int maxDrawBuffers,
55                                                    bool usesPixelLocalStorage)
56     : TIntermTraverser(true, false, false),
57       mMaxDrawBuffers(maxDrawBuffers),
58       mEnablesBlendFuncExtended(
59           IsExtensionEnabled(extBehavior, TExtension::EXT_blend_func_extended)),
60       mUsesPixelLocalStorage(usesPixelLocalStorage),
61       mUsesFragDepth(false)
62 {}
63 
visitSymbol(TIntermSymbol * symbol)64 void ValidateOutputsTraverser::visitSymbol(TIntermSymbol *symbol)
65 {
66     if (symbol->variable().symbolType() == SymbolType::Empty)
67         return;
68 
69     if (mVisitedSymbols.count(symbol->uniqueId().get()) == 1)
70         return;
71 
72     mVisitedSymbols.insert(symbol->uniqueId().get());
73 
74     TQualifier qualifier = symbol->getQualifier();
75     if (qualifier == EvqFragmentOut)
76     {
77         if (symbol->getType().getLayoutQualifier().location != -1)
78         {
79             mOutputs.push_back(symbol);
80         }
81         else if (symbol->getType().getLayoutQualifier().yuv == true)
82         {
83             mYuvOutputs.push_back(symbol);
84         }
85         else
86         {
87             mUnspecifiedLocationOutputs.push_back(symbol);
88         }
89     }
90     else if (qualifier == EvqFragDepth)
91     {
92         mUsesFragDepth = true;
93     }
94 }
95 
validate(TDiagnostics * diagnostics) const96 void ValidateOutputsTraverser::validate(TDiagnostics *diagnostics) const
97 {
98     ASSERT(diagnostics);
99     OutputVector validOutputs(mMaxDrawBuffers, nullptr);
100     OutputVector validSecondaryOutputs(mMaxDrawBuffers, nullptr);
101 
102     for (const auto &symbol : mOutputs)
103     {
104         const TType &type = symbol->getType();
105         ASSERT(!type.isArrayOfArrays());  // Disallowed in GLSL ES 3.10 section 4.3.6.
106         const size_t elementCount =
107             static_cast<size_t>(type.isArray() ? type.getOutermostArraySize() : 1u);
108         const size_t location = static_cast<size_t>(type.getLayoutQualifier().location);
109 
110         ASSERT(type.getLayoutQualifier().location != -1);
111 
112         OutputVector *validOutputsToUse = &validOutputs;
113         // The default index is 0, so we only assign the output to secondary outputs in case the
114         // index is explicitly set to 1.
115         if (type.getLayoutQualifier().index == 1)
116         {
117             validOutputsToUse = &validSecondaryOutputs;
118         }
119 
120         if (location + elementCount <= validOutputsToUse->size())
121         {
122             for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++)
123             {
124                 const size_t offsetLocation = location + elementIndex;
125                 if ((*validOutputsToUse)[offsetLocation])
126                 {
127                     std::stringstream strstr = sh::InitializeStream<std::stringstream>();
128                     strstr << "conflicting output locations with previously defined output '"
129                            << (*validOutputsToUse)[offsetLocation]->getName() << "'";
130                     error(*symbol, strstr.str().c_str(), diagnostics);
131                 }
132                 else
133                 {
134                     (*validOutputsToUse)[offsetLocation] = symbol;
135                 }
136             }
137         }
138         else
139         {
140             if (elementCount > 0)
141             {
142                 error(*symbol,
143                       elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
144                                        : "output location must be < MAX_DRAW_BUFFERS",
145                       diagnostics);
146             }
147         }
148     }
149 
150     if ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) ||
151         mUnspecifiedLocationOutputs.size() > 1)
152     {
153         const char *unspecifiedLocationErrorMessage = nullptr;
154         if (!mEnablesBlendFuncExtended)
155         {
156             unspecifiedLocationErrorMessage =
157                 "must explicitly specify all locations when using multiple fragment outputs";
158         }
159         else if (mUsesPixelLocalStorage)
160         {
161             unspecifiedLocationErrorMessage =
162                 "must explicitly specify all locations when using multiple fragment outputs and "
163                 "pixel local storage, even if EXT_blend_func_extended is enabled";
164         }
165         if (unspecifiedLocationErrorMessage != nullptr)
166         {
167             for (const auto &symbol : mUnspecifiedLocationOutputs)
168             {
169                 error(*symbol, unspecifiedLocationErrorMessage, diagnostics);
170             }
171         }
172     }
173 
174     if (!mYuvOutputs.empty() && (mYuvOutputs.size() > 1 || mUsesFragDepth || !mOutputs.empty() ||
175                                  !mUnspecifiedLocationOutputs.empty()))
176     {
177         for (const auto &symbol : mYuvOutputs)
178         {
179             error(*symbol,
180                   "not allowed to specify yuv qualifier when using depth or multiple color "
181                   "fragment outputs",
182                   diagnostics);
183         }
184     }
185 }
186 
187 }  // anonymous namespace
188 
ValidateOutputs(TIntermBlock * root,const TExtensionBehavior & extBehavior,int maxDrawBuffers,bool usesPixelLocalStorage,TDiagnostics * diagnostics)189 bool ValidateOutputs(TIntermBlock *root,
190                      const TExtensionBehavior &extBehavior,
191                      int maxDrawBuffers,
192                      bool usesPixelLocalStorage,
193                      TDiagnostics *diagnostics)
194 {
195     ValidateOutputsTraverser validateOutputs(extBehavior, maxDrawBuffers, usesPixelLocalStorage);
196     root->traverse(&validateOutputs);
197     int numErrorsBefore = diagnostics->numErrors();
198     validateOutputs.validate(diagnostics);
199     return (diagnostics->numErrors() == numErrorsBefore);
200 }
201 
202 }  // namespace sh
203