Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name RewritePixelLocalStorage.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +sse2 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/angle/targets/translator -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/angle/targets/translator -resource-dir /usr/lib/llvm-21/lib/clang/21 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D _GLIBCXX_ASSERTIONS -D DEBUG=1 -D ANGLE_PLATFORM_EXPORT= -D __NDK_FPABI__= -D ANGLE_SKIP_DXGI_1_2_CHECK -D ANGLE_ENABLE_KEYEDMUTEX -D ANGLE_TRANSLATOR_ESSL_ONLY -D ANGLE_DISABLE_POOL_ALLOC -D ANGLE_ENABLE_APPLE_WORKAROUNDS -D ANGLE_ENABLE_ESSL -D ANGLE_ENABLE_GLSL -D ANGLE_ENABLE_HLSL -D ANGLE_ENABLE_SHARE_CONTEXT_LOCK=1 -D ANGLE_VMA_VERSION=2003000 -D CR_CLANG_REVISION="llvmorg-16-init-6578-g0d30e92f-2" -D DYNAMIC_ANNOTATIONS_ENABLED=0 -D NOMINMAX -D UNICODE -D WINVER=0x0A00 -D _ATL_NO_OPENGL -D _CRT_NONSTDC_NO_WARNINGS -D _CRT_RAND_S -D _CRT_SECURE_NO_DEPRECATE -D _HAS_EXCEPTIONS=0 -D _SCL_SECURE_NO_DEPRECATE -D _SECURE_ATL -D _UNICODE -D _WINSOCK_DEPRECATED_NO_WARNINGS -I /root/firefox-clang/gfx/angle/targets/translator -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/angle/targets/translator -I /root/firefox-clang/gfx/angle/checkout/include -I /root/firefox-clang/gfx/angle/checkout/src -I /root/firefox-clang/gfx/angle/checkout/src/common/third_party/base -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-final-dtor-non-final-class -Wno-implicit-const-int-float-conversion -Wno-range-loop-construct -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2025-06-27-100320-3286336-1 -x c++ /root/firefox-clang/gfx/angle/checkout/src/compiler/translator/tree_ops/RewritePixelLocalStorage.cpp
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
18namespace sh
19{
20namespace
21{
22constexpr 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
38constexpr 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.
55template <typename T>
56class 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"
))
;
3
Assuming 'plsSymbol' is null
4
'?' condition is false
5
Assuming the condition is true
6
'?' condition is true
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())"
))
;
7
Called C++ object pointer is null
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.
93class 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.
218class 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.
594class 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);
1
Passing value via 1st parameter 'plsSymbol'
2
Calling 'PLSBackingStoreMap::find'
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
792bool 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