File: | root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp |
Warning: | line 76, column 9 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | // | |||
2 | // Copyright 2022 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 | ||||
7 | #include "compiler/translator/tree_ops/RewritePixelLocalStorage.h" | |||
8 | ||||
9 | #include "common/angleutils.h" | |||
10 | #include "compiler/translator/StaticType.h" | |||
11 | #include "compiler/translator/SymbolTable.h" | |||
12 | #include "compiler/translator/tree_ops/MonomorphizeUnsupportedFunctions.h" | |||
13 | #include "compiler/translator/tree_util/BuiltIn.h" | |||
14 | #include "compiler/translator/tree_util/FindMain.h" | |||
15 | #include "compiler/translator/tree_util/IntermNode_util.h" | |||
16 | #include "compiler/translator/tree_util/IntermTraverse.h" | |||
17 | ||||
18 | namespace sh | |||
19 | { | |||
20 | namespace | |||
21 | { | |||
22 | constexpr static TBasicType DataTypeOfPLSType(TBasicType plsType) | |||
23 | { | |||
24 | switch (plsType) | |||
25 | { | |||
26 | case EbtPixelLocalANGLE: | |||
27 | return EbtFloat; | |||
28 | case EbtIPixelLocalANGLE: | |||
29 | return EbtInt; | |||
30 | case EbtUPixelLocalANGLE: | |||
31 | return EbtUInt; | |||
32 | default: | |||
33 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 33, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 33 << ")"; } while (0); | |||
34 | return EbtVoid; | |||
35 | } | |||
36 | } | |||
37 | ||||
38 | constexpr static TBasicType DataTypeOfImageType(TBasicType imageType) | |||
39 | { | |||
40 | switch (imageType) | |||
41 | { | |||
42 | case EbtImage2D: | |||
43 | return EbtFloat; | |||
44 | case EbtIImage2D: | |||
45 | return EbtInt; | |||
46 | case EbtUImage2D: | |||
47 | return EbtUInt; | |||
48 | default: | |||
49 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 49, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 49 << ")"; } while (0); | |||
50 | return EbtVoid; | |||
51 | } | |||
52 | } | |||
53 | ||||
54 | // Maps PLS symbols to a backing store. | |||
55 | template <typename T> | |||
56 | class PLSBackingStoreMap | |||
57 | { | |||
58 | public: | |||
59 | // Sets the given variable as the backing storage for the plsSymbol's binding point. An entry | |||
60 | // must not already exist in the map for this binding point. | |||
61 | void insertNew(TIntermSymbol *plsSymbol, const T &backingStore) | |||
62 | { | |||
63 | ASSERT(plsSymbol)(plsSymbol ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 63, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 63 << "): " << "plsSymbol" )); | |||
64 | ASSERT(IsPixelLocal(plsSymbol->getBasicType()))(IsPixelLocal(plsSymbol->getBasicType()) ? static_cast< void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage( ::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv:: LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 64, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 64 << "): " << "IsPixelLocal(plsSymbol->getBasicType())" )); | |||
65 | int binding = plsSymbol->getType().getLayoutQualifier().binding; | |||
66 | ASSERT(binding >= 0)(binding >= 0 ? static_cast<void>(0) : (!((::gl::priv ::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 66, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 66 << "): " << "binding >= 0" )); | |||
67 | auto result = mMap.insert({binding, backingStore}); | |||
68 | ASSERT(result.second)(result.second ? static_cast<void>(0) : (!((::gl::priv:: ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 68, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 68 << "): " << "result.second" )); // Ensure an image didn't already exist for this symbol. | |||
69 | } | |||
70 | ||||
71 | // Looks up the backing store for the given plsSymbol's binding point. An entry must already | |||
72 | // exist in the map for this binding point. | |||
73 | const T &find(TIntermSymbol *plsSymbol) | |||
74 | { | |||
75 | ASSERT(plsSymbol)(plsSymbol ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 75, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 75 << "): " << "plsSymbol" )); | |||
76 | ASSERT(IsPixelLocal(plsSymbol->getBasicType()))(IsPixelLocal(plsSymbol->getBasicType()) ? static_cast< void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage( ::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv:: LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 76, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 76 << "): " << "IsPixelLocal(plsSymbol->getBasicType())" )); | |||
| ||||
77 | int binding = plsSymbol->getType().getLayoutQualifier().binding; | |||
78 | ASSERT(binding >= 0)(binding >= 0 ? static_cast<void>(0) : (!((::gl::priv ::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 78, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 78 << "): " << "binding >= 0" )); | |||
79 | auto iter = mMap.find(binding); | |||
80 | ASSERT(iter != mMap.end())(iter != mMap.end() ? static_cast<void>(0) : (!((::gl:: priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 80, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 80 << "): " << "iter != mMap.end()" )); // Ensure PLSImages already exist for this symbol. | |||
81 | return iter->second; | |||
82 | } | |||
83 | ||||
84 | const std::map<int, T> &bindingOrderedMap() const { return mMap; } | |||
85 | ||||
86 | private: | |||
87 | // Use std::map so the backing stores are ordered by binding when we iterate. | |||
88 | std::map<int, T> mMap; | |||
89 | }; | |||
90 | ||||
91 | // Base class for rewriting high level PLS operations to AST operations specified by | |||
92 | // ShPixelLocalStorageType. | |||
93 | class RewritePLSTraverser : public TIntermTraverser | |||
94 | { | |||
95 | public: | |||
96 | RewritePLSTraverser(TCompiler *compiler, | |||
97 | TSymbolTable &symbolTable, | |||
98 | const ShCompileOptions &compileOptions, | |||
99 | int shaderVersion) | |||
100 | : TIntermTraverser(true, false, false, &symbolTable), | |||
101 | mCompiler(compiler), | |||
102 | mCompileOptions(&compileOptions), | |||
103 | mShaderVersion(shaderVersion) | |||
104 | {} | |||
105 | ||||
106 | bool visitDeclaration(Visit, TIntermDeclaration *decl) override | |||
107 | { | |||
108 | TIntermTyped *declVariable = (decl->getSequence())->front()->getAsTyped(); | |||
109 | ASSERT(declVariable)(declVariable ? static_cast<void>(0) : (!((::gl::priv:: ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 109, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 109 << "): " << "declVariable" )); | |||
110 | ||||
111 | if (!IsPixelLocal(declVariable->getBasicType())) | |||
112 | { | |||
113 | return true; | |||
114 | } | |||
115 | ||||
116 | // PLS is not allowed in arrays. | |||
117 | ASSERT(!declVariable->isArray())(!declVariable->isArray() ? static_cast<void>(0) : ( !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 117, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 117 << "): " << "!declVariable->isArray()" )); | |||
118 | ||||
119 | // This visitDeclaration doesn't get called for function arguments, and opaque types can | |||
120 | // otherwise only be uniforms. | |||
121 | ASSERT(declVariable->getQualifier() == EvqUniform)(declVariable->getQualifier() == EvqUniform ? static_cast< void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage( ::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv:: LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 121, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 121 << "): " << "declVariable->getQualifier() == EvqUniform" )); | |||
122 | ||||
123 | TIntermSymbol *plsSymbol = declVariable->getAsSymbolNode(); | |||
124 | ASSERT(plsSymbol)(plsSymbol ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 124, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 124 << "): " << "plsSymbol" )); | |||
125 | ||||
126 | visitPLSDeclaration(plsSymbol); | |||
127 | ||||
128 | return false; | |||
129 | } | |||
130 | ||||
131 | bool visitAggregate(Visit, TIntermAggregate *aggregate) override | |||
132 | { | |||
133 | if (!BuiltInGroup::IsPixelLocal(aggregate->getOp())) | |||
134 | { | |||
135 | return true; | |||
136 | } | |||
137 | ||||
138 | const TIntermSequence &args = *aggregate->getSequence(); | |||
139 | ASSERT(args.size() >= 1)(args.size() >= 1 ? static_cast<void>(0) : (!((::gl:: priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 139, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 139 << "): " << "args.size() >= 1" )); | |||
140 | TIntermSymbol *plsSymbol = args[0]->getAsSymbolNode(); | |||
141 | ||||
142 | // Rewrite pixelLocalLoadANGLE -> imageLoad. | |||
143 | if (aggregate->getOp() == EOpPixelLocalLoadANGLE) | |||
144 | { | |||
145 | visitPLSLoad(plsSymbol); | |||
146 | return false; // No need to recurse since this node is being dropped. | |||
147 | } | |||
148 | ||||
149 | // Rewrite pixelLocalStoreANGLE -> imageStore. | |||
150 | if (aggregate->getOp() == EOpPixelLocalStoreANGLE) | |||
151 | { | |||
152 | // Also hoist the 'value' expression into a temp. In the event of | |||
153 | // "pixelLocalStoreANGLE(..., pixelLocalLoadANGLE(...))", this ensures the load occurs | |||
154 | // _before_ any potential barriers required by the subclass. | |||
155 | // | |||
156 | // NOTE: It is generally unsafe to hoist function arguments due to short circuiting, | |||
157 | // e.g., "if (false && function(...))", but pixelLocalStoreANGLE returns type void, so | |||
158 | // it is safe in this particular case. | |||
159 | TType *valueType = new TType(DataTypeOfPLSType(plsSymbol->getBasicType()), | |||
160 | plsSymbol->getPrecision(), EvqTemporary, 4); | |||
161 | TVariable *valueVar = CreateTempVariable(mSymbolTable, valueType); | |||
162 | TIntermDeclaration *valueDecl = | |||
163 | CreateTempInitDeclarationNode(valueVar, args[1]->getAsTyped()); | |||
164 | valueDecl->traverse(this); // Rewrite any potential pixelLocalLoadANGLEs in valueDecl. | |||
165 | insertStatementInParentBlock(valueDecl); | |||
166 | ||||
167 | visitPLSStore(plsSymbol, valueVar); | |||
168 | return false; // No need to recurse since this node is being dropped. | |||
169 | } | |||
170 | ||||
171 | return true; | |||
172 | } | |||
173 | ||||
174 | // Called after rewrite. Injects one-time setup code that needs to run before any PLS accesses. | |||
175 | virtual void injectSetupCode(TCompiler *, | |||
176 | TSymbolTable &, | |||
177 | const ShCompileOptions &, | |||
178 | TIntermBlock *mainBody, | |||
179 | size_t plsBeginPosition) | |||
180 | {} | |||
181 | ||||
182 | // Called after rewrite. Injects one-time finalization code that needs to run after all PLS. | |||
183 | virtual void injectFinalizeCode(TCompiler *, | |||
184 | TSymbolTable &, | |||
185 | const ShCompileOptions &, | |||
186 | TIntermBlock *mainBody, | |||
187 | size_t plsEndPosition) | |||
188 | {} | |||
189 | ||||
190 | TVariable *globalPixelCoord() const { return mGlobalPixelCoord; } | |||
191 | ||||
192 | protected: | |||
193 | virtual void visitPLSDeclaration(TIntermSymbol *plsSymbol) = 0; | |||
194 | virtual void visitPLSLoad(TIntermSymbol *plsSymbol) = 0; | |||
195 | virtual void visitPLSStore(TIntermSymbol *plsSymbol, TVariable *value) = 0; | |||
196 | ||||
197 | void ensureGlobalPixelCoordDeclared() | |||
198 | { | |||
199 | // Insert a global to hold the pixel coordinate as soon as we see PLS declared. This will be | |||
200 | // initialized at the beginning of main(). | |||
201 | if (!mGlobalPixelCoord) | |||
202 | { | |||
203 | TType *coordType = new TType(EbtInt, EbpHigh, EvqGlobal, 2); | |||
204 | mGlobalPixelCoord = CreateTempVariable(mSymbolTable, coordType); | |||
205 | insertStatementInParentBlock(CreateTempDeclarationNode(mGlobalPixelCoord)); | |||
206 | } | |||
207 | } | |||
208 | ||||
209 | const TCompiler *const mCompiler; | |||
210 | const ShCompileOptions *const mCompileOptions; | |||
211 | const int mShaderVersion; | |||
212 | ||||
213 | // Stores the shader invocation's pixel coordinate as "ivec2(floor(gl_FragCoord.xy))". | |||
214 | TVariable *mGlobalPixelCoord = nullptr; | |||
215 | }; | |||
216 | ||||
217 | // Rewrites high level PLS operations to shader image operations. | |||
218 | class RewritePLSToImagesTraverser : public RewritePLSTraverser | |||
219 | { | |||
220 | public: | |||
221 | RewritePLSToImagesTraverser(TCompiler *compiler, | |||
222 | TSymbolTable &symbolTable, | |||
223 | const ShCompileOptions &compileOptions, | |||
224 | int shaderVersion) | |||
225 | : RewritePLSTraverser(compiler, symbolTable, compileOptions, shaderVersion) | |||
226 | {} | |||
227 | ||||
228 | private: | |||
229 | void visitPLSDeclaration(TIntermSymbol *plsSymbol) override | |||
230 | { | |||
231 | // Replace the PLS declaration with an image2D. | |||
232 | ensureGlobalPixelCoordDeclared(); | |||
233 | TVariable *image2D = createPLSImageReplacement(plsSymbol); | |||
234 | mImages.insertNew(plsSymbol, image2D); | |||
235 | queueReplacement(new TIntermDeclaration({new TIntermSymbol(image2D)}), | |||
236 | OriginalNode::IS_DROPPED); | |||
237 | } | |||
238 | ||||
239 | // Do all PLS formats need to be packed into r32f, r32i, or r32ui image2Ds? | |||
240 | bool needsR32Packing() const | |||
241 | { | |||
242 | return mCompileOptions->pls.type == ShPixelLocalStorageType::ImageStoreR32PackedFormats; | |||
243 | } | |||
244 | ||||
245 | // Creates an image2D that replaces a pixel local storage handle. | |||
246 | TVariable *createPLSImageReplacement(const TIntermSymbol *plsSymbol) | |||
247 | { | |||
248 | ASSERT(plsSymbol)(plsSymbol ? static_cast<void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage (::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv ::LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 248, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 248 << "): " << "plsSymbol" )); | |||
249 | ASSERT(IsPixelLocal(plsSymbol->getBasicType()))(IsPixelLocal(plsSymbol->getBasicType()) ? static_cast< void>(0) : (!((::gl::priv::ShouldCreatePlatformLogMessage( ::gl::LOG_FATAL))) ? static_cast<void>(0) : ::gl::priv:: LogMessageVoidify() & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 249, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 249 << "): " << "IsPixelLocal(plsSymbol->getBasicType())" )); | |||
250 | ||||
251 | TType *imageType = new TType(plsSymbol->getType()); | |||
252 | ||||
253 | TLayoutQualifier layoutQualifier = imageType->getLayoutQualifier(); | |||
254 | switch (layoutQualifier.imageInternalFormat) | |||
255 | { | |||
256 | case TLayoutImageInternalFormat::EiifRGBA8: | |||
257 | if (needsR32Packing()) | |||
258 | { | |||
259 | layoutQualifier.imageInternalFormat = EiifR32UI; | |||
260 | imageType->setPrecision(EbpHigh); | |||
261 | imageType->setBasicType(EbtUImage2D); | |||
262 | } | |||
263 | else | |||
264 | { | |||
265 | imageType->setBasicType(EbtImage2D); | |||
266 | } | |||
267 | break; | |||
268 | case TLayoutImageInternalFormat::EiifRGBA8I: | |||
269 | if (needsR32Packing()) | |||
270 | { | |||
271 | layoutQualifier.imageInternalFormat = EiifR32I; | |||
272 | imageType->setPrecision(EbpHigh); | |||
273 | } | |||
274 | imageType->setBasicType(EbtIImage2D); | |||
275 | break; | |||
276 | case TLayoutImageInternalFormat::EiifRGBA8UI: | |||
277 | if (needsR32Packing()) | |||
278 | { | |||
279 | layoutQualifier.imageInternalFormat = EiifR32UI; | |||
280 | imageType->setPrecision(EbpHigh); | |||
281 | } | |||
282 | imageType->setBasicType(EbtUImage2D); | |||
283 | break; | |||
284 | case TLayoutImageInternalFormat::EiifR32F: | |||
285 | imageType->setBasicType(EbtImage2D); | |||
286 | break; | |||
287 | case TLayoutImageInternalFormat::EiifR32UI: | |||
288 | imageType->setBasicType(EbtUImage2D); | |||
289 | break; | |||
290 | default: | |||
291 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 291, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 291 << ")"; } while (0); | |||
292 | } | |||
293 | layoutQualifier.rasterOrdered = mCompileOptions->pls.fragmentSynchronizationType == | |||
294 | ShFragmentSynchronizationType::RasterizerOrderViews_D3D; | |||
295 | imageType->setLayoutQualifier(layoutQualifier); | |||
296 | ||||
297 | TMemoryQualifier memoryQualifier{}; | |||
298 | memoryQualifier.coherent = true; | |||
299 | memoryQualifier.restrictQualifier = true; | |||
300 | memoryQualifier.volatileQualifier = false; | |||
301 | // TODO(anglebug.com/7279): Maybe we could walk the tree first and see which PLS is used | |||
302 | // how. If the PLS is never loaded, we could add a writeonly qualifier, for example. | |||
303 | memoryQualifier.readonly = false; | |||
304 | memoryQualifier.writeonly = false; | |||
305 | imageType->setMemoryQualifier(memoryQualifier); | |||
306 | ||||
307 | const TVariable &plsVar = plsSymbol->variable(); | |||
308 | return new TVariable(plsVar.uniqueId(), plsVar.name(), plsVar.symbolType(), | |||
309 | plsVar.extensions(), imageType); | |||
310 | } | |||
311 | ||||
312 | void visitPLSLoad(TIntermSymbol *plsSymbol) override | |||
313 | { | |||
314 | // Replace the pixelLocalLoadANGLE with imageLoad. | |||
315 | TVariable *image2D = mImages.find(plsSymbol); | |||
316 | ASSERT(mGlobalPixelCoord)(mGlobalPixelCoord ? static_cast<void>(0) : (!((::gl::priv ::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 316, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 316 << "): " << "mGlobalPixelCoord" )); | |||
317 | TIntermTyped *pls = CreateBuiltInFunctionCallNode( | |||
318 | "imageLoad", {new TIntermSymbol(image2D), new TIntermSymbol(mGlobalPixelCoord)}, | |||
319 | *mSymbolTable, 310); | |||
320 | pls = unpackImageDataIfNecessary(pls, plsSymbol, image2D); | |||
321 | queueReplacement(pls, OriginalNode::IS_DROPPED); | |||
322 | } | |||
323 | ||||
324 | // Unpacks the raw PLS data if the output shader language needs r32* packing. | |||
325 | TIntermTyped *unpackImageDataIfNecessary(TIntermTyped *data, | |||
326 | TIntermSymbol *plsSymbol, | |||
327 | TVariable *image2D) | |||
328 | { | |||
329 | TLayoutImageInternalFormat plsFormat = | |||
330 | plsSymbol->getType().getLayoutQualifier().imageInternalFormat; | |||
331 | TLayoutImageInternalFormat imageFormat = | |||
332 | image2D->getType().getLayoutQualifier().imageInternalFormat; | |||
333 | if (plsFormat == imageFormat) | |||
334 | { | |||
335 | return data; // This PLS storage isn't packed. | |||
336 | } | |||
337 | ASSERT(needsR32Packing())(needsR32Packing() ? static_cast<void>(0) : (!((::gl::priv ::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 337, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 337 << "): " << "needsR32Packing()" )); | |||
338 | switch (plsFormat) | |||
339 | { | |||
340 | case EiifRGBA8: | |||
341 | // Unpack and normalize r,g,b,a from a single 32-bit unsigned int: | |||
342 | // | |||
343 | // unpackUnorm4x8(data.r) | |||
344 | // | |||
345 | data = CreateBuiltInFunctionCallNode("unpackUnorm4x8", {CreateSwizzle(data, 0)}, | |||
346 | *mSymbolTable, 310); | |||
347 | break; | |||
348 | case EiifRGBA8I: | |||
349 | case EiifRGBA8UI: | |||
350 | { | |||
351 | constexpr unsigned shifts[] = {24, 16, 8, 0}; | |||
352 | // Unpack r,g,b,a form a single (signed or unsigned) 32-bit int. Shift left, | |||
353 | // then right, to preserve the sign for ints. (highp integers are exactly | |||
354 | // 32-bit, two's compliment.) | |||
355 | // | |||
356 | // data.rrrr << uvec4(24, 16, 8, 0) >> 24u | |||
357 | // | |||
358 | data = CreateSwizzle(data, 0, 0, 0, 0); | |||
359 | data = new TIntermBinary(EOpBitShiftLeft, data, CreateUVecNode(shifts, 4, EbpHigh)); | |||
360 | data = new TIntermBinary(EOpBitShiftRight, data, CreateUIntNode(24)); | |||
361 | break; | |||
362 | } | |||
363 | default: | |||
364 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 364, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 364 << ")"; } while (0); | |||
365 | } | |||
366 | return data; | |||
367 | } | |||
368 | ||||
369 | void visitPLSStore(TIntermSymbol *plsSymbol, TVariable *value) override | |||
370 | { | |||
371 | TVariable *image2D = mImages.find(plsSymbol); | |||
372 | TIntermTyped *packedData = clampAndPackPLSDataIfNecessary(value, plsSymbol, image2D); | |||
373 | ||||
374 | // Surround the store with memoryBarrierImage calls in order to ensure dependent stores and | |||
375 | // loads in a single shader invocation are coherent. From the ES 3.1 spec: | |||
376 | // | |||
377 | // Using variables declared as "coherent" guarantees only that the results of stores will | |||
378 | // be immediately visible to shader invocations using similarly-declared variables; | |||
379 | // calling MemoryBarrier is required to ensure that the stores are visible to other | |||
380 | // operations. | |||
381 | // | |||
382 | insertStatementsInParentBlock( | |||
383 | {CreateBuiltInFunctionCallNode("memoryBarrierImage", {}, *mSymbolTable, | |||
384 | 310)}, // Before. | |||
385 | {CreateBuiltInFunctionCallNode("memoryBarrierImage", {}, *mSymbolTable, | |||
386 | 310)}); // After. | |||
387 | ||||
388 | // Rewrite the pixelLocalStoreANGLE with imageStore. | |||
389 | ASSERT(mGlobalPixelCoord)(mGlobalPixelCoord ? static_cast<void>(0) : (!((::gl::priv ::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 389, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 389 << "): " << "mGlobalPixelCoord" )); | |||
390 | queueReplacement( | |||
391 | CreateBuiltInFunctionCallNode( | |||
392 | "imageStore", | |||
393 | {new TIntermSymbol(image2D), new TIntermSymbol(mGlobalPixelCoord), packedData}, | |||
394 | *mSymbolTable, 310), | |||
395 | OriginalNode::IS_DROPPED); | |||
396 | } | |||
397 | ||||
398 | // Packs the PLS to raw data if the output shader language needs r32* packing. | |||
399 | TIntermTyped *clampAndPackPLSDataIfNecessary(TVariable *plsVar, | |||
400 | TIntermSymbol *plsSymbol, | |||
401 | TVariable *image2D) | |||
402 | { | |||
403 | TLayoutImageInternalFormat plsFormat = | |||
404 | plsSymbol->getType().getLayoutQualifier().imageInternalFormat; | |||
405 | // anglebug.com/7524: Storing to integer formats with values larger than can be represented | |||
406 | // is specified differently on different APIs. Clamp integer formats here to make it uniform | |||
407 | // and more GL-like. | |||
408 | switch (plsFormat) | |||
409 | { | |||
410 | case EiifRGBA8I: | |||
411 | { | |||
412 | // Clamp r,g,b,a to their min/max 8-bit values: | |||
413 | // | |||
414 | // plsVar = clamp(plsVar, -128, 127) & 0xff | |||
415 | // | |||
416 | TIntermTyped *newPLSValue = CreateBuiltInFunctionCallNode( | |||
417 | "clamp", | |||
418 | {new TIntermSymbol(plsVar), CreateIndexNode(-128), CreateIndexNode(127)}, | |||
419 | *mSymbolTable, mShaderVersion); | |||
420 | insertStatementInParentBlock(CreateTempAssignmentNode(plsVar, newPLSValue)); | |||
421 | break; | |||
422 | } | |||
423 | case EiifRGBA8UI: | |||
424 | { | |||
425 | // Clamp r,g,b,a to their max 8-bit values: | |||
426 | // | |||
427 | // plsVar = min(plsVar, 255) | |||
428 | // | |||
429 | TIntermTyped *newPLSValue = CreateBuiltInFunctionCallNode( | |||
430 | "min", {new TIntermSymbol(plsVar), CreateUIntNode(255)}, *mSymbolTable, | |||
431 | mShaderVersion); | |||
432 | insertStatementInParentBlock(CreateTempAssignmentNode(plsVar, newPLSValue)); | |||
433 | break; | |||
434 | } | |||
435 | default: | |||
436 | break; | |||
437 | } | |||
438 | TIntermTyped *result = new TIntermSymbol(plsVar); | |||
439 | TLayoutImageInternalFormat imageFormat = | |||
440 | image2D->getType().getLayoutQualifier().imageInternalFormat; | |||
441 | if (plsFormat == imageFormat) | |||
442 | { | |||
443 | return result; // This PLS storage isn't packed. | |||
444 | } | |||
445 | ASSERT(needsR32Packing())(needsR32Packing() ? static_cast<void>(0) : (!((::gl::priv ::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 445, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 445 << "): " << "needsR32Packing()" )); | |||
446 | switch (plsFormat) | |||
447 | { | |||
448 | case EiifRGBA8: | |||
449 | { | |||
450 | if (mCompileOptions->passHighpToPackUnormSnormBuiltins) | |||
451 | { | |||
452 | // anglebug.com/7527: unpackUnorm4x8 doesn't work on Pixel 4 when passed | |||
453 | // a mediump vec4. Use an intermediate highp vec4. | |||
454 | // | |||
455 | // It's safe to inject a variable here because it happens right before | |||
456 | // pixelLocalStoreANGLE, which returns type void. (See visitAggregate.) | |||
457 | TType *highpType = new TType(EbtFloat, EbpHigh, EvqTemporary, 4); | |||
458 | TVariable *workaroundHighpVar = CreateTempVariable(mSymbolTable, highpType); | |||
459 | insertStatementInParentBlock( | |||
460 | CreateTempInitDeclarationNode(workaroundHighpVar, result)); | |||
461 | result = new TIntermSymbol(workaroundHighpVar); | |||
462 | } | |||
463 | ||||
464 | // Denormalize and pack r,g,b,a into a single 32-bit unsigned int: | |||
465 | // | |||
466 | // packUnorm4x8(workaroundHighpVar) | |||
467 | // | |||
468 | result = | |||
469 | CreateBuiltInFunctionCallNode("packUnorm4x8", {result}, *mSymbolTable, 310); | |||
470 | break; | |||
471 | } | |||
472 | case EiifRGBA8I: | |||
473 | case EiifRGBA8UI: | |||
474 | { | |||
475 | if (plsFormat == EiifRGBA8I) | |||
476 | { | |||
477 | // Mask off extra sign bits beyond 8. | |||
478 | // | |||
479 | // plsVar &= 0xff | |||
480 | // | |||
481 | insertStatementInParentBlock(new TIntermBinary( | |||
482 | EOpBitwiseAndAssign, new TIntermSymbol(plsVar), CreateIndexNode(0xff))); | |||
483 | } | |||
484 | // Pack r,g,b,a into a single 32-bit (signed or unsigned) int: | |||
485 | // | |||
486 | // r | (g << 8) | (b << 16) | (a << 24) | |||
487 | // | |||
488 | auto shiftComponent = [=](int componentIdx) { | |||
489 | return new TIntermBinary(EOpBitShiftLeft, | |||
490 | CreateSwizzle(new TIntermSymbol(plsVar), componentIdx), | |||
491 | CreateUIntNode(componentIdx * 8)); | |||
492 | }; | |||
493 | result = CreateSwizzle(result, 0); | |||
494 | result = new TIntermBinary(EOpBitwiseOr, result, shiftComponent(1)); | |||
495 | result = new TIntermBinary(EOpBitwiseOr, result, shiftComponent(2)); | |||
496 | result = new TIntermBinary(EOpBitwiseOr, result, shiftComponent(3)); | |||
497 | break; | |||
498 | } | |||
499 | default: | |||
500 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 500, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 500 << ")"; } while (0); | |||
501 | } | |||
502 | // Convert the packed data to a {u,i}vec4 for imageStore. | |||
503 | TType imageStoreType(DataTypeOfImageType(image2D->getType().getBasicType()), 4); | |||
504 | return TIntermAggregate::CreateConstructor(imageStoreType, {result}); | |||
505 | } | |||
506 | ||||
507 | void injectSetupCode(TCompiler *compiler, | |||
508 | TSymbolTable &symbolTable, | |||
509 | const ShCompileOptions &compileOptions, | |||
510 | TIntermBlock *mainBody, | |||
511 | size_t plsBeginPosition) override | |||
512 | { | |||
513 | // When PLS is implemented with images, early_fragment_tests ensure that depth/stencil | |||
514 | // can also block stores to PLS. | |||
515 | compiler->specifyEarlyFragmentTests(); | |||
516 | ||||
517 | // Delimit the beginning of a per-pixel critical section, if supported. This makes pixel | |||
518 | // local storage coherent. | |||
519 | // | |||
520 | // Either: GL_NV_fragment_shader_interlock | |||
521 | // GL_INTEL_fragment_shader_ordering | |||
522 | // GL_ARB_fragment_shader_interlock (may compile to | |||
523 | // SPV_EXT_fragment_shader_interlock) | |||
524 | switch (compileOptions.pls.fragmentSynchronizationType) | |||
525 | { | |||
526 | // ROVs don't need explicit synchronization calls. | |||
527 | case ShFragmentSynchronizationType::RasterizerOrderViews_D3D: | |||
528 | case ShFragmentSynchronizationType::NotSupported: | |||
529 | break; | |||
530 | case ShFragmentSynchronizationType::FragmentShaderInterlock_NV_GL: | |||
531 | mainBody->insertStatement( | |||
532 | plsBeginPosition, | |||
533 | CreateBuiltInFunctionCallNode("beginInvocationInterlockNV", {}, symbolTable, | |||
534 | kESSLInternalBackendBuiltIns)); | |||
535 | break; | |||
536 | case ShFragmentSynchronizationType::FragmentShaderOrdering_INTEL_GL: | |||
537 | mainBody->insertStatement( | |||
538 | plsBeginPosition, | |||
539 | CreateBuiltInFunctionCallNode("beginFragmentShaderOrderingINTEL", {}, | |||
540 | symbolTable, kESSLInternalBackendBuiltIns)); | |||
541 | break; | |||
542 | case ShFragmentSynchronizationType::FragmentShaderInterlock_ARB_GL: | |||
543 | mainBody->insertStatement( | |||
544 | plsBeginPosition, | |||
545 | CreateBuiltInFunctionCallNode("beginInvocationInterlockARB", {}, symbolTable, | |||
546 | kESSLInternalBackendBuiltIns)); | |||
547 | break; | |||
548 | default: | |||
549 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 549, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 549 << ")"; } while (0); | |||
550 | } | |||
551 | } | |||
552 | ||||
553 | void injectFinalizeCode(TCompiler *, | |||
554 | TSymbolTable &symbolTable, | |||
555 | const ShCompileOptions &compileOptions, | |||
556 | TIntermBlock *mainBody, | |||
557 | size_t plsEndPosition) override | |||
558 | { | |||
559 | // Delimit the end of the PLS critical section, if required. | |||
560 | // | |||
561 | // Either: GL_NV_fragment_shader_interlock | |||
562 | // GL_ARB_fragment_shader_interlock (may compile to | |||
563 | // SPV_EXT_fragment_shader_interlock) | |||
564 | switch (compileOptions.pls.fragmentSynchronizationType) | |||
565 | { | |||
566 | // ROVs don't need explicit synchronization calls. | |||
567 | case ShFragmentSynchronizationType::RasterizerOrderViews_D3D: | |||
568 | // GL_INTEL_fragment_shader_ordering doesn't have an "end()" call. | |||
569 | case ShFragmentSynchronizationType::FragmentShaderOrdering_INTEL_GL: | |||
570 | case ShFragmentSynchronizationType::NotSupported: | |||
571 | break; | |||
572 | case ShFragmentSynchronizationType::FragmentShaderInterlock_NV_GL: | |||
573 | ||||
574 | mainBody->insertStatement( | |||
575 | plsEndPosition, | |||
576 | CreateBuiltInFunctionCallNode("endInvocationInterlockNV", {}, symbolTable, | |||
577 | kESSLInternalBackendBuiltIns)); | |||
578 | break; | |||
579 | case ShFragmentSynchronizationType::FragmentShaderInterlock_ARB_GL: | |||
580 | mainBody->insertStatement( | |||
581 | plsEndPosition, | |||
582 | CreateBuiltInFunctionCallNode("endInvocationInterlockARB", {}, symbolTable, | |||
583 | kESSLInternalBackendBuiltIns)); | |||
584 | break; | |||
585 | default: | |||
586 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 586, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 586 << ")"; } while (0); | |||
587 | } | |||
588 | } | |||
589 | ||||
590 | PLSBackingStoreMap<TVariable *> mImages; | |||
591 | }; | |||
592 | ||||
593 | // Rewrites high level PLS operations to framebuffer fetch operations. | |||
594 | class RewritePLSToFramebufferFetchTraverser : public RewritePLSTraverser | |||
595 | { | |||
596 | public: | |||
597 | RewritePLSToFramebufferFetchTraverser(TCompiler *compiler, | |||
598 | TSymbolTable &symbolTable, | |||
599 | const ShCompileOptions &compileOptions, | |||
600 | int shaderVersion) | |||
601 | : RewritePLSTraverser(compiler, symbolTable, compileOptions, shaderVersion) | |||
602 | {} | |||
603 | ||||
604 | void visitPLSDeclaration(TIntermSymbol *plsSymbol) override | |||
605 | { | |||
606 | // Replace the PLS declaration with a framebuffer attachment. | |||
607 | PLSAttachment attachment(mCompiler, mSymbolTable, *mCompileOptions, plsSymbol->variable()); | |||
608 | mPLSAttachments.insertNew(plsSymbol, attachment); | |||
609 | insertStatementInParentBlock( | |||
610 | new TIntermDeclaration({new TIntermSymbol(attachment.fragmentVar)})); | |||
611 | queueReplacement(CreateTempDeclarationNode(attachment.accessVar), OriginalNode::IS_DROPPED); | |||
612 | } | |||
613 | ||||
614 | void visitPLSLoad(TIntermSymbol *plsSymbol) override | |||
615 | { | |||
616 | // Read our temporary accessVar. | |||
617 | const PLSAttachment &attachment = mPLSAttachments.find(plsSymbol); | |||
618 | queueReplacement(attachment.expandAccessVar(), OriginalNode::IS_DROPPED); | |||
619 | } | |||
620 | ||||
621 | void visitPLSStore(TIntermSymbol *plsSymbol, TVariable *value) override | |||
622 | { | |||
623 | // Set our temporary accessVar. | |||
624 | const PLSAttachment &attachment = mPLSAttachments.find(plsSymbol); | |||
| ||||
625 | queueReplacement(CreateTempAssignmentNode(attachment.accessVar, attachment.swizzle(value)), | |||
626 | OriginalNode::IS_DROPPED); | |||
627 | } | |||
628 | ||||
629 | void injectSetupCode(TCompiler *compiler, | |||
630 | TSymbolTable &symbolTable, | |||
631 | const ShCompileOptions &compileOptions, | |||
632 | TIntermBlock *mainBody, | |||
633 | size_t plsBeginPosition) override | |||
634 | { | |||
635 | // [OpenGL ES Version 3.0.6, 3.9.2.3 "Shader Output"]: Any colors, or color components, | |||
636 | // associated with a fragment that are not written by the fragment shader are undefined. | |||
637 | // | |||
638 | // [EXT_shader_framebuffer_fetch]: Prior to fragment shading, fragment outputs declared | |||
639 | // inout are populated with the value last written to the framebuffer at the same(x, y, | |||
640 | // sample) position. | |||
641 | // | |||
642 | // It's unclear from the EXT_shader_framebuffer_fetch spec whether inout fragment variables | |||
643 | // become undefined if not explicitly written, but either way, when this compiles to subpass | |||
644 | // loads in Vulkan, we definitely get undefined behavior if PLS variables are not written. | |||
645 | // | |||
646 | // To make sure every PLS variable gets written, we read them all before PLS operations, | |||
647 | // then write them all back out after all PLS is complete. | |||
648 | std::vector<TIntermNode *> plsPreloads; | |||
649 | plsPreloads.reserve(mPLSAttachments.bindingOrderedMap().size()); | |||
650 | for (const auto &entry : mPLSAttachments.bindingOrderedMap()) | |||
651 | { | |||
652 | const PLSAttachment &attachment = entry.second; | |||
653 | plsPreloads.push_back( | |||
654 | CreateTempAssignmentNode(attachment.accessVar, attachment.swizzleFragmentVar())); | |||
655 | } | |||
656 | mainBody->getSequence()->insert(mainBody->getSequence()->begin() + plsBeginPosition, | |||
657 | plsPreloads.begin(), plsPreloads.end()); | |||
658 | } | |||
659 | ||||
660 | void injectFinalizeCode(TCompiler *, | |||
661 | TSymbolTable &symbolTable, | |||
662 | const ShCompileOptions &compileOptions, | |||
663 | TIntermBlock *mainBody, | |||
664 | size_t plsEndPosition) override | |||
665 | { | |||
666 | std::vector<TIntermNode *> plsWrites; | |||
667 | plsWrites.reserve(mPLSAttachments.bindingOrderedMap().size()); | |||
668 | for (const auto &entry : mPLSAttachments.bindingOrderedMap()) | |||
669 | { | |||
670 | const PLSAttachment &attachment = entry.second; | |||
671 | plsWrites.push_back(new TIntermBinary(EOpAssign, attachment.swizzleFragmentVar(), | |||
672 | new TIntermSymbol(attachment.accessVar))); | |||
673 | } | |||
674 | mainBody->getSequence()->insert(mainBody->getSequence()->begin() + plsEndPosition, | |||
675 | plsWrites.begin(), plsWrites.end()); | |||
676 | } | |||
677 | ||||
678 | private: | |||
679 | struct PLSAttachment | |||
680 | { | |||
681 | PLSAttachment(const TCompiler *compiler, | |||
682 | TSymbolTable *symbolTable, | |||
683 | const ShCompileOptions &compileOptions, | |||
684 | const TVariable &plsVar) | |||
685 | { | |||
686 | const TType &plsType = plsVar.getType(); | |||
687 | ||||
688 | TType *accessVarType; | |||
689 | switch (plsType.getLayoutQualifier().imageInternalFormat) | |||
690 | { | |||
691 | default: | |||
692 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 692, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 692 << ")"; } while (0); | |||
693 | [[fallthrough]]; | |||
694 | case EiifRGBA8: | |||
695 | accessVarType = new TType(EbtFloat, 4); | |||
696 | break; | |||
697 | case EiifRGBA8I: | |||
698 | accessVarType = new TType(EbtInt, 4); | |||
699 | break; | |||
700 | case EiifRGBA8UI: | |||
701 | accessVarType = new TType(EbtUInt, 4); | |||
702 | break; | |||
703 | case EiifR32F: | |||
704 | accessVarType = new TType(EbtFloat, 1); | |||
705 | break; | |||
706 | case EiifR32UI: | |||
707 | accessVarType = new TType(EbtUInt, 1); | |||
708 | break; | |||
709 | } | |||
710 | accessVarType->setPrecision(plsType.getPrecision()); | |||
711 | accessVar = CreateTempVariable(symbolTable, accessVarType); | |||
712 | ||||
713 | // Qualcomm seems to want fragment outputs to be 4-component vectors, and produces a | |||
714 | // compile error from "inout uint". Our Metal translator also saturates color outputs to | |||
715 | // 4 components. And since the spec also seems silent on how many components an output | |||
716 | // must have, we always use 4. | |||
717 | TType *fragmentVarType = new TType(accessVarType->getBasicType(), 4); | |||
718 | fragmentVarType->setPrecision(plsType.getPrecision()); | |||
719 | fragmentVarType->setQualifier(EvqFragmentInOut); | |||
720 | ||||
721 | // PLS attachments are bound in reverse order from the rear. | |||
722 | TLayoutQualifier layoutQualifier = TLayoutQualifier::Create(); | |||
723 | layoutQualifier.location = | |||
724 | compiler->getResources().MaxCombinedDrawBuffersAndPixelLocalStoragePlanes - | |||
725 | plsType.getLayoutQualifier().binding - 1; | |||
726 | layoutQualifier.locationsSpecified = 1; | |||
727 | if (compileOptions.pls.fragmentSynchronizationType == | |||
728 | ShFragmentSynchronizationType::NotSupported) | |||
729 | { | |||
730 | // We're using EXT_shader_framebuffer_fetch_non_coherent, which requires the | |||
731 | // "noncoherent" qualifier. | |||
732 | layoutQualifier.noncoherent = true; | |||
733 | } | |||
734 | fragmentVarType->setLayoutQualifier(layoutQualifier); | |||
735 | ||||
736 | fragmentVar = new TVariable(plsVar.uniqueId(), plsVar.name(), plsVar.symbolType(), | |||
737 | plsVar.extensions(), fragmentVarType); | |||
738 | } | |||
739 | ||||
740 | // Expands our accessVar to 4 components, regardless of the size of the pixel local storage | |||
741 | // internalformat. | |||
742 | TIntermTyped *expandAccessVar() const | |||
743 | { | |||
744 | TIntermTyped *expanded = new TIntermSymbol(accessVar); | |||
745 | if (accessVar->getType().getNominalSize() == 1) | |||
746 | { | |||
747 | switch (accessVar->getType().getBasicType()) | |||
748 | { | |||
749 | case EbtFloat: | |||
750 | expanded = TIntermAggregate::CreateConstructor( // "vec4(r, 0, 0, 1)" | |||
751 | TType(EbtFloat, 4), | |||
752 | {expanded, CreateFloatNode(0, EbpHigh), CreateFloatNode(0, EbpHigh), | |||
753 | CreateFloatNode(1, EbpHigh)}); | |||
754 | break; | |||
755 | case EbtUInt: | |||
756 | expanded = TIntermAggregate::CreateConstructor( // "uvec4(r, 0, 0, 1)" | |||
757 | TType(EbtUInt, 4), | |||
758 | {expanded, CreateUIntNode(0), CreateUIntNode(0), CreateUIntNode(1)}); | |||
759 | break; | |||
760 | default: | |||
761 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 761, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 761 << ")"; } while (0); | |||
762 | break; | |||
763 | } | |||
764 | } | |||
765 | return expanded; | |||
766 | } | |||
767 | ||||
768 | // Swizzles a variable down to the same number of components as the PLS internalformat. | |||
769 | TIntermTyped *swizzle(TVariable *var) const | |||
770 | { | |||
771 | TIntermTyped *swizzled = new TIntermSymbol(var); | |||
772 | if (var->getType().getNominalSize() != accessVar->getType().getNominalSize()) | |||
773 | { | |||
774 | ASSERT(var->getType().getNominalSize() > accessVar->getType().getNominalSize())(var->getType().getNominalSize() > accessVar->getType ().getNominalSize() ? static_cast<void>(0) : (!((::gl:: priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL))) ? static_cast <void>(0) : ::gl::priv::LogMessageVoidify() & (::gl ::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 774, ::gl::LOG_FATAL).stream()) << "\t! Assert failed in " << __FUNCTION__ << " (" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 774 << "): " << "var->getType().getNominalSize() > accessVar->getType().getNominalSize()" )); | |||
775 | TVector swizzleOffsets{0, 1, 2, 3}; | |||
776 | swizzleOffsets.resize(accessVar->getType().getNominalSize()); | |||
777 | swizzled = new TIntermSwizzle(swizzled, swizzleOffsets); | |||
778 | } | |||
779 | return swizzled; | |||
780 | } | |||
781 | ||||
782 | TIntermTyped *swizzleFragmentVar() const { return swizzle(fragmentVar); } | |||
783 | ||||
784 | TVariable *fragmentVar; | |||
785 | TVariable *accessVar; | |||
786 | }; | |||
787 | ||||
788 | PLSBackingStoreMap<PLSAttachment> mPLSAttachments; | |||
789 | }; | |||
790 | } // anonymous namespace | |||
791 | ||||
792 | bool RewritePixelLocalStorage(TCompiler *compiler, | |||
793 | TIntermBlock *root, | |||
794 | TSymbolTable &symbolTable, | |||
795 | const ShCompileOptions &compileOptions, | |||
796 | int shaderVersion) | |||
797 | { | |||
798 | // If any functions take PLS arguments, monomorphize the functions by removing said parameters | |||
799 | // and making the PLS calls from main() instead, using the global uniform from the call site | |||
800 | // instead of the function argument. This is necessary because function arguments don't carry | |||
801 | // the necessary "binding" or "format" layout qualifiers. | |||
802 | if (!MonomorphizeUnsupportedFunctions( | |||
803 | compiler, root, &symbolTable, compileOptions, | |||
804 | UnsupportedFunctionArgsBitSet{UnsupportedFunctionArgs::PixelLocalStorage})) | |||
805 | { | |||
806 | return false; | |||
807 | } | |||
808 | ||||
809 | TIntermBlock *mainBody = FindMainBody(root); | |||
810 | ||||
811 | std::unique_ptr<RewritePLSTraverser> traverser; | |||
812 | switch (compileOptions.pls.type) | |||
813 | { | |||
814 | case ShPixelLocalStorageType::ImageStoreR32PackedFormats: | |||
815 | case ShPixelLocalStorageType::ImageStoreNativeFormats: | |||
816 | traverser = std::make_unique<RewritePLSToImagesTraverser>( | |||
817 | compiler, symbolTable, compileOptions, shaderVersion); | |||
818 | break; | |||
819 | case ShPixelLocalStorageType::FramebufferFetch: | |||
820 | traverser = std::make_unique<RewritePLSToFramebufferFetchTraverser>( | |||
821 | compiler, symbolTable, compileOptions, shaderVersion); | |||
822 | break; | |||
823 | default: | |||
824 | UNREACHABLE()do { !((::gl::priv::ShouldCreatePlatformLogMessage(::gl::LOG_FATAL ))) ? static_cast<void>(0) : ::gl::priv::LogMessageVoidify () & (::gl::LogMessage("/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" , __FUNCTION__, 824, ::gl::LOG_FATAL).stream()) << "\t! Unreachable reached: " << __FUNCTION__ << "(" << "/root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp" << ":" << 824 << ")"; } while (0); | |||
825 | return false; | |||
826 | } | |||
827 | ||||
828 | // Rewrite PLS operations to image operations. | |||
829 | root->traverse(traverser.get()); | |||
830 | if (!traverser->updateTree(compiler, root)) | |||
831 | { | |||
832 | return false; | |||
833 | } | |||
834 | ||||
835 | // Inject the code that needs to run before and after all PLS operations. | |||
836 | // TODO(anglebug.com/7279): Inject these functions in a tight critical section, instead of | |||
837 | // just locking the entire main() function: | |||
838 | // - Monomorphize all PLS calls into main(). | |||
839 | // - Insert begin/end calls around the first/last PLS calls (and outside of flow control). | |||
840 | traverser->injectSetupCode(compiler, symbolTable, compileOptions, mainBody, 0); | |||
841 | traverser->injectFinalizeCode(compiler, symbolTable, compileOptions, mainBody, | |||
842 | mainBody->getChildCount()); | |||
843 | ||||
844 | if (traverser->globalPixelCoord()) | |||
845 | { | |||
846 | // Initialize the global pixel coord at the beginning of main(): | |||
847 | // | |||
848 | // pixelCoord = ivec2(floor(gl_FragCoord.xy)); | |||
849 | // | |||
850 | TIntermTyped *exp; | |||
851 | exp = ReferenceBuiltInVariable(ImmutableString("gl_FragCoord"), symbolTable, shaderVersion); | |||
852 | exp = CreateSwizzle(exp, 0, 1); | |||
853 | exp = CreateBuiltInFunctionCallNode("floor", {exp}, symbolTable, shaderVersion); | |||
854 | exp = TIntermAggregate::CreateConstructor(TType(EbtInt, 2), {exp}); | |||
855 | exp = CreateTempAssignmentNode(traverser->globalPixelCoord(), exp); | |||
856 | mainBody->insertStatement(0, exp); | |||
857 | } | |||
858 | ||||
859 | return compiler->validateAST(root); | |||
860 | } | |||
861 | } // namespace sh |