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