xref: /aosp_15_r20/external/skia/src/sksl/codegen/SkSLSPIRVCodeGenerator.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2016 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/codegen/SkSLSPIRVCodeGenerator.h"
9*c8dee2aaSAndroid Build Coastguard Worker 
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkEnumBitMask.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkChecksum.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTHash.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTraceEvent.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/GLSL.std.450.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLAnalysis.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLBuiltinTypes.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLCompiler.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLConstantFolder.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLContext.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLDefines.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLErrorReporter.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLIntrinsicList.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLMemoryLayout.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLOperator.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLOutputStream.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPool.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLPosition.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLProgramSettings.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLStringStream.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/SkSLUtil.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/analysis/SkSLSpecialization.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/codegen/SkSLCodeGenerator.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBinaryExpression.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLBlock.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructor.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorArrayCast.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorCompound.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorCompoundCast.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorScalarCast.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLConstructorSplat.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLDoStatement.h"
48*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpression.h"
49*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExpressionStatement.h"
50*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLExtension.h"
51*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFieldAccess.h"
52*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFieldSymbol.h"
53*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLForStatement.h"
54*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionCall.h"
55*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDeclaration.h"
56*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLFunctionDefinition.h"
57*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIRNode.h"
58*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIfStatement.h"
59*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLIndexExpression.h"
60*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLInterfaceBlock.h"
61*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLayout.h"
62*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLLiteral.h"
63*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifierFlags.h"
64*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLModifiersDeclaration.h"
65*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPoison.h"
66*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPostfixExpression.h"
67*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLPrefixExpression.h"
68*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgram.h"
69*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLProgramElement.h"
70*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLReturnStatement.h"
71*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSetting.h"
72*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLStatement.h"
73*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwitchCase.h"
74*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwitchStatement.h"
75*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSwizzle.h"
76*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSymbol.h"
77*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLSymbolTable.h"
78*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLTernaryExpression.h"
79*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLType.h"
80*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVarDeclarations.h"
81*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariable.h"
82*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/ir/SkSLVariableReference.h"
83*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/spirv.h"
84*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/transform/SkSLTransform.h"
85*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkBitSet.h"
86*c8dee2aaSAndroid Build Coastguard Worker 
87*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
88*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
89*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
90*c8dee2aaSAndroid Build Coastguard Worker #include <ctype.h>
91*c8dee2aaSAndroid Build Coastguard Worker #include <functional>
92*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
93*c8dee2aaSAndroid Build Coastguard Worker #include <set>
94*c8dee2aaSAndroid Build Coastguard Worker #include <string>
95*c8dee2aaSAndroid Build Coastguard Worker #include <string_view>
96*c8dee2aaSAndroid Build Coastguard Worker #include <tuple>
97*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
98*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
99*c8dee2aaSAndroid Build Coastguard Worker 
100*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
101*c8dee2aaSAndroid Build Coastguard Worker 
102*c8dee2aaSAndroid Build Coastguard Worker #define kLast_Capability SpvCapabilityMultiViewport
103*c8dee2aaSAndroid Build Coastguard Worker 
104*c8dee2aaSAndroid Build Coastguard Worker constexpr int DEVICE_FRAGCOORDS_BUILTIN = -1000;
105*c8dee2aaSAndroid Build Coastguard Worker constexpr int DEVICE_CLOCKWISE_BUILTIN  = -1001;
106*c8dee2aaSAndroid Build Coastguard Worker static constexpr SkSL::Layout kDefaultTypeLayout;
107*c8dee2aaSAndroid Build Coastguard Worker 
108*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
109*c8dee2aaSAndroid Build Coastguard Worker 
110*c8dee2aaSAndroid Build Coastguard Worker enum class ProgramKind : int8_t;
111*c8dee2aaSAndroid Build Coastguard Worker 
112*c8dee2aaSAndroid Build Coastguard Worker enum class StorageClass {
113*c8dee2aaSAndroid Build Coastguard Worker     kUniformConstant,
114*c8dee2aaSAndroid Build Coastguard Worker     kInput,
115*c8dee2aaSAndroid Build Coastguard Worker     kUniform,
116*c8dee2aaSAndroid Build Coastguard Worker     kStorageBuffer,
117*c8dee2aaSAndroid Build Coastguard Worker     kOutput,
118*c8dee2aaSAndroid Build Coastguard Worker     kWorkgroup,
119*c8dee2aaSAndroid Build Coastguard Worker     kCrossWorkgroup,
120*c8dee2aaSAndroid Build Coastguard Worker     kPrivate,
121*c8dee2aaSAndroid Build Coastguard Worker     kFunction,
122*c8dee2aaSAndroid Build Coastguard Worker     kGeneric,
123*c8dee2aaSAndroid Build Coastguard Worker     kPushConstant,
124*c8dee2aaSAndroid Build Coastguard Worker     kAtomicCounter,
125*c8dee2aaSAndroid Build Coastguard Worker     kImage,
126*c8dee2aaSAndroid Build Coastguard Worker };
127*c8dee2aaSAndroid Build Coastguard Worker 
get_storage_class_spv_id(StorageClass storageClass)128*c8dee2aaSAndroid Build Coastguard Worker static SpvStorageClass get_storage_class_spv_id(StorageClass storageClass) {
129*c8dee2aaSAndroid Build Coastguard Worker     switch (storageClass) {
130*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kUniformConstant: return SpvStorageClassUniformConstant;
131*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kInput: return SpvStorageClassInput;
132*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kUniform: return SpvStorageClassUniform;
133*c8dee2aaSAndroid Build Coastguard Worker         // Note: In SPIR-V 1.3, a storage buffer can be declared with the "StorageBuffer"
134*c8dee2aaSAndroid Build Coastguard Worker         // storage class and the "Block" decoration and the <1.3 approach we use here ("Uniform"
135*c8dee2aaSAndroid Build Coastguard Worker         // storage class and the "BufferBlock" decoration) is deprecated. Since we target SPIR-V
136*c8dee2aaSAndroid Build Coastguard Worker         // 1.0, we have to use the deprecated approach which is well supported in Vulkan and
137*c8dee2aaSAndroid Build Coastguard Worker         // addresses SkSL use cases (notably SkSL currently doesn't support pointer features that
138*c8dee2aaSAndroid Build Coastguard Worker         // would benefit from SPV_KHR_variable_pointers capabilities).
139*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kStorageBuffer: return SpvStorageClassUniform;
140*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kOutput: return SpvStorageClassOutput;
141*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kWorkgroup: return SpvStorageClassWorkgroup;
142*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kCrossWorkgroup: return SpvStorageClassCrossWorkgroup;
143*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kPrivate: return SpvStorageClassPrivate;
144*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kFunction: return SpvStorageClassFunction;
145*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kGeneric: return SpvStorageClassGeneric;
146*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kPushConstant: return SpvStorageClassPushConstant;
147*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kAtomicCounter: return SpvStorageClassAtomicCounter;
148*c8dee2aaSAndroid Build Coastguard Worker         case StorageClass::kImage: return SpvStorageClassImage;
149*c8dee2aaSAndroid Build Coastguard Worker     }
150*c8dee2aaSAndroid Build Coastguard Worker 
151*c8dee2aaSAndroid Build Coastguard Worker     SkUNREACHABLE;
152*c8dee2aaSAndroid Build Coastguard Worker }
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker class SPIRVCodeGenerator : public CodeGenerator {
155*c8dee2aaSAndroid Build Coastguard Worker public:
156*c8dee2aaSAndroid Build Coastguard Worker     // We reserve an impossible SpvId as a sentinel. (NA meaning none, n/a, etc.)
157*c8dee2aaSAndroid Build Coastguard Worker     static constexpr SpvId NA = (SpvId)-1;
158*c8dee2aaSAndroid Build Coastguard Worker 
159*c8dee2aaSAndroid Build Coastguard Worker     class LValue {
160*c8dee2aaSAndroid Build Coastguard Worker     public:
~LValue()161*c8dee2aaSAndroid Build Coastguard Worker         virtual ~LValue() {}
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker         // returns a pointer to the lvalue, if possible. If the lvalue cannot be directly referenced
164*c8dee2aaSAndroid Build Coastguard Worker         // by a pointer (e.g. vector swizzles), returns NA.
getPointer()165*c8dee2aaSAndroid Build Coastguard Worker         virtual SpvId getPointer() { return NA; }
166*c8dee2aaSAndroid Build Coastguard Worker 
167*c8dee2aaSAndroid Build Coastguard Worker         // Returns true if a valid pointer returned by getPointer represents a memory object
168*c8dee2aaSAndroid Build Coastguard Worker         // (see https://github.com/KhronosGroup/SPIRV-Tools/issues/2892). Has no meaning if
169*c8dee2aaSAndroid Build Coastguard Worker         // getPointer() returns NA.
isMemoryObjectPointer() const170*c8dee2aaSAndroid Build Coastguard Worker         virtual bool isMemoryObjectPointer() const { return true; }
171*c8dee2aaSAndroid Build Coastguard Worker 
172*c8dee2aaSAndroid Build Coastguard Worker         // Applies a swizzle to the components of the LValue, if possible. This is used to create
173*c8dee2aaSAndroid Build Coastguard Worker         // LValues that are swizzes-of-swizzles. Non-swizzle LValues can just return false.
applySwizzle(const ComponentArray & components,const Type & newType)174*c8dee2aaSAndroid Build Coastguard Worker         virtual bool applySwizzle(const ComponentArray& components, const Type& newType) {
175*c8dee2aaSAndroid Build Coastguard Worker             return false;
176*c8dee2aaSAndroid Build Coastguard Worker         }
177*c8dee2aaSAndroid Build Coastguard Worker 
178*c8dee2aaSAndroid Build Coastguard Worker         // Returns the storage class of the lvalue.
179*c8dee2aaSAndroid Build Coastguard Worker         virtual StorageClass storageClass() const = 0;
180*c8dee2aaSAndroid Build Coastguard Worker 
181*c8dee2aaSAndroid Build Coastguard Worker         virtual SpvId load(OutputStream& out) = 0;
182*c8dee2aaSAndroid Build Coastguard Worker 
183*c8dee2aaSAndroid Build Coastguard Worker         virtual void store(SpvId value, OutputStream& out) = 0;
184*c8dee2aaSAndroid Build Coastguard Worker     };
185*c8dee2aaSAndroid Build Coastguard Worker 
SPIRVCodeGenerator(const Context * context,const ShaderCaps * caps,const Program * program,OutputStream * out)186*c8dee2aaSAndroid Build Coastguard Worker     SPIRVCodeGenerator(const Context* context,
187*c8dee2aaSAndroid Build Coastguard Worker                        const ShaderCaps* caps,
188*c8dee2aaSAndroid Build Coastguard Worker                        const Program* program,
189*c8dee2aaSAndroid Build Coastguard Worker                        OutputStream* out)
190*c8dee2aaSAndroid Build Coastguard Worker             : CodeGenerator(context, caps, program, out) {}
191*c8dee2aaSAndroid Build Coastguard Worker 
192*c8dee2aaSAndroid Build Coastguard Worker     bool generateCode() override;
193*c8dee2aaSAndroid Build Coastguard Worker 
194*c8dee2aaSAndroid Build Coastguard Worker private:
195*c8dee2aaSAndroid Build Coastguard Worker     enum IntrinsicOpcodeKind {
196*c8dee2aaSAndroid Build Coastguard Worker         kGLSL_STD_450_IntrinsicOpcodeKind,
197*c8dee2aaSAndroid Build Coastguard Worker         kSPIRV_IntrinsicOpcodeKind,
198*c8dee2aaSAndroid Build Coastguard Worker         kSpecial_IntrinsicOpcodeKind,
199*c8dee2aaSAndroid Build Coastguard Worker         kInvalid_IntrinsicOpcodeKind,
200*c8dee2aaSAndroid Build Coastguard Worker     };
201*c8dee2aaSAndroid Build Coastguard Worker 
202*c8dee2aaSAndroid Build Coastguard Worker     enum SpecialIntrinsic {
203*c8dee2aaSAndroid Build Coastguard Worker         kAtan_SpecialIntrinsic,
204*c8dee2aaSAndroid Build Coastguard Worker         kClamp_SpecialIntrinsic,
205*c8dee2aaSAndroid Build Coastguard Worker         kMatrixCompMult_SpecialIntrinsic,
206*c8dee2aaSAndroid Build Coastguard Worker         kMax_SpecialIntrinsic,
207*c8dee2aaSAndroid Build Coastguard Worker         kMin_SpecialIntrinsic,
208*c8dee2aaSAndroid Build Coastguard Worker         kMix_SpecialIntrinsic,
209*c8dee2aaSAndroid Build Coastguard Worker         kMod_SpecialIntrinsic,
210*c8dee2aaSAndroid Build Coastguard Worker         kDFdy_SpecialIntrinsic,
211*c8dee2aaSAndroid Build Coastguard Worker         kSaturate_SpecialIntrinsic,
212*c8dee2aaSAndroid Build Coastguard Worker         kSampledImage_SpecialIntrinsic,
213*c8dee2aaSAndroid Build Coastguard Worker         kSmoothStep_SpecialIntrinsic,
214*c8dee2aaSAndroid Build Coastguard Worker         kStep_SpecialIntrinsic,
215*c8dee2aaSAndroid Build Coastguard Worker         kSubpassLoad_SpecialIntrinsic,
216*c8dee2aaSAndroid Build Coastguard Worker         kTexture_SpecialIntrinsic,
217*c8dee2aaSAndroid Build Coastguard Worker         kTextureGrad_SpecialIntrinsic,
218*c8dee2aaSAndroid Build Coastguard Worker         kTextureLod_SpecialIntrinsic,
219*c8dee2aaSAndroid Build Coastguard Worker         kTextureRead_SpecialIntrinsic,
220*c8dee2aaSAndroid Build Coastguard Worker         kTextureWrite_SpecialIntrinsic,
221*c8dee2aaSAndroid Build Coastguard Worker         kTextureWidth_SpecialIntrinsic,
222*c8dee2aaSAndroid Build Coastguard Worker         kTextureHeight_SpecialIntrinsic,
223*c8dee2aaSAndroid Build Coastguard Worker         kAtomicAdd_SpecialIntrinsic,
224*c8dee2aaSAndroid Build Coastguard Worker         kAtomicLoad_SpecialIntrinsic,
225*c8dee2aaSAndroid Build Coastguard Worker         kAtomicStore_SpecialIntrinsic,
226*c8dee2aaSAndroid Build Coastguard Worker         kStorageBarrier_SpecialIntrinsic,
227*c8dee2aaSAndroid Build Coastguard Worker         kWorkgroupBarrier_SpecialIntrinsic,
228*c8dee2aaSAndroid Build Coastguard Worker     };
229*c8dee2aaSAndroid Build Coastguard Worker 
230*c8dee2aaSAndroid Build Coastguard Worker     enum class Precision {
231*c8dee2aaSAndroid Build Coastguard Worker         kDefault,
232*c8dee2aaSAndroid Build Coastguard Worker         kRelaxed,
233*c8dee2aaSAndroid Build Coastguard Worker     };
234*c8dee2aaSAndroid Build Coastguard Worker 
235*c8dee2aaSAndroid Build Coastguard Worker     struct TempVar {
236*c8dee2aaSAndroid Build Coastguard Worker         SpvId spvId;
237*c8dee2aaSAndroid Build Coastguard Worker         const Type* type;
238*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<SPIRVCodeGenerator::LValue> lvalue;
239*c8dee2aaSAndroid Build Coastguard Worker     };
240*c8dee2aaSAndroid Build Coastguard Worker 
241*c8dee2aaSAndroid Build Coastguard Worker     /**
242*c8dee2aaSAndroid Build Coastguard Worker      * Pass in the type to automatically add a RelaxedPrecision decoration for the id when
243*c8dee2aaSAndroid Build Coastguard Worker      * appropriate, or null to never add one.
244*c8dee2aaSAndroid Build Coastguard Worker      */
245*c8dee2aaSAndroid Build Coastguard Worker     SpvId nextId(const Type* type);
246*c8dee2aaSAndroid Build Coastguard Worker 
247*c8dee2aaSAndroid Build Coastguard Worker     SpvId nextId(Precision precision);
248*c8dee2aaSAndroid Build Coastguard Worker 
249*c8dee2aaSAndroid Build Coastguard Worker     SpvId getType(const Type& type);
250*c8dee2aaSAndroid Build Coastguard Worker 
251*c8dee2aaSAndroid Build Coastguard Worker     SpvId getType(const Type& type, const Layout& typeLayout, const MemoryLayout& memoryLayout);
252*c8dee2aaSAndroid Build Coastguard Worker 
253*c8dee2aaSAndroid Build Coastguard Worker     SpvId getFunctionType(const FunctionDeclaration& function);
254*c8dee2aaSAndroid Build Coastguard Worker 
255*c8dee2aaSAndroid Build Coastguard Worker     SpvId getFunctionParameterType(const Type& parameterType, const Layout& parameterLayout);
256*c8dee2aaSAndroid Build Coastguard Worker 
257*c8dee2aaSAndroid Build Coastguard Worker     SpvId getPointerType(const Type& type, StorageClass storageClass);
258*c8dee2aaSAndroid Build Coastguard Worker 
259*c8dee2aaSAndroid Build Coastguard Worker     SpvId getPointerType(const Type& type,
260*c8dee2aaSAndroid Build Coastguard Worker                          const Layout& typeLayout,
261*c8dee2aaSAndroid Build Coastguard Worker                          const MemoryLayout& memoryLayout,
262*c8dee2aaSAndroid Build Coastguard Worker                          StorageClass storageClass);
263*c8dee2aaSAndroid Build Coastguard Worker 
264*c8dee2aaSAndroid Build Coastguard Worker     StorageClass getStorageClass(const Expression& expr);
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker     TArray<SpvId> getAccessChain(const Expression& expr, OutputStream& out);
267*c8dee2aaSAndroid Build Coastguard Worker 
268*c8dee2aaSAndroid Build Coastguard Worker     void writeLayout(const Layout& layout, SpvId target, Position pos);
269*c8dee2aaSAndroid Build Coastguard Worker 
270*c8dee2aaSAndroid Build Coastguard Worker     void writeFieldLayout(const Layout& layout, SpvId target, int member);
271*c8dee2aaSAndroid Build Coastguard Worker 
272*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeStruct(const Type& type, const MemoryLayout& memoryLayout);
273*c8dee2aaSAndroid Build Coastguard Worker 
274*c8dee2aaSAndroid Build Coastguard Worker     void writeProgramElement(const ProgramElement& pe, OutputStream& out);
275*c8dee2aaSAndroid Build Coastguard Worker 
276*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeInterfaceBlock(const InterfaceBlock& intf, bool appendRTFlip = true);
277*c8dee2aaSAndroid Build Coastguard Worker 
278*c8dee2aaSAndroid Build Coastguard Worker     void writeFunctionStart(const FunctionDeclaration& f, OutputStream& out);
279*c8dee2aaSAndroid Build Coastguard Worker 
280*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeFunctionDeclaration(const FunctionDeclaration& f, OutputStream& out);
281*c8dee2aaSAndroid Build Coastguard Worker 
282*c8dee2aaSAndroid Build Coastguard Worker     void writeFunction(const FunctionDefinition& f, OutputStream& out);
283*c8dee2aaSAndroid Build Coastguard Worker 
284*c8dee2aaSAndroid Build Coastguard Worker     // Writes the function with the defined specializationIndex, if the index is -1, then it is
285*c8dee2aaSAndroid Build Coastguard Worker     // assumed that the function has no specializations.
286*c8dee2aaSAndroid Build Coastguard Worker     void writeFunctionInstantiation(const FunctionDefinition& f,
287*c8dee2aaSAndroid Build Coastguard Worker                                     Analysis::SpecializationIndex specializationIndex,
288*c8dee2aaSAndroid Build Coastguard Worker                                     const Analysis::SpecializedParameters* specializedParams,
289*c8dee2aaSAndroid Build Coastguard Worker                                     OutputStream& out);
290*c8dee2aaSAndroid Build Coastguard Worker 
291*c8dee2aaSAndroid Build Coastguard Worker     bool writeGlobalVarDeclaration(ProgramKind kind, const VarDeclaration& v);
292*c8dee2aaSAndroid Build Coastguard Worker 
293*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeGlobalVar(ProgramKind kind, StorageClass, const Variable& v);
294*c8dee2aaSAndroid Build Coastguard Worker 
295*c8dee2aaSAndroid Build Coastguard Worker     void writeVarDeclaration(const VarDeclaration& var, OutputStream& out);
296*c8dee2aaSAndroid Build Coastguard Worker 
297*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeVariableReference(const VariableReference& ref, OutputStream& out);
298*c8dee2aaSAndroid Build Coastguard Worker 
299*c8dee2aaSAndroid Build Coastguard Worker     int findUniformFieldIndex(const Variable& var) const;
300*c8dee2aaSAndroid Build Coastguard Worker 
301*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> getLValue(const Expression& value, OutputStream& out);
302*c8dee2aaSAndroid Build Coastguard Worker 
303*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeExpression(const Expression& expr, OutputStream& out);
304*c8dee2aaSAndroid Build Coastguard Worker 
305*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeIntrinsicCall(const FunctionCall& c, OutputStream& out);
306*c8dee2aaSAndroid Build Coastguard Worker 
307*c8dee2aaSAndroid Build Coastguard Worker     void writeFunctionCallArgument(TArray<SpvId>& argumentList,
308*c8dee2aaSAndroid Build Coastguard Worker                                    const FunctionCall& call,
309*c8dee2aaSAndroid Build Coastguard Worker                                    int argIndex,
310*c8dee2aaSAndroid Build Coastguard Worker                                    std::vector<TempVar>* tempVars,
311*c8dee2aaSAndroid Build Coastguard Worker                                    const SkBitSet* specializedParams,
312*c8dee2aaSAndroid Build Coastguard Worker                                    OutputStream& out);
313*c8dee2aaSAndroid Build Coastguard Worker 
314*c8dee2aaSAndroid Build Coastguard Worker     void copyBackTempVars(const std::vector<TempVar>& tempVars, OutputStream& out);
315*c8dee2aaSAndroid Build Coastguard Worker 
316*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeFunctionCall(const FunctionCall& c, OutputStream& out);
317*c8dee2aaSAndroid Build Coastguard Worker 
318*c8dee2aaSAndroid Build Coastguard Worker 
319*c8dee2aaSAndroid Build Coastguard Worker     void writeGLSLExtendedInstruction(const Type& type, SpvId id, SpvId floatInst,
320*c8dee2aaSAndroid Build Coastguard Worker                                       SpvId signedInst, SpvId unsignedInst,
321*c8dee2aaSAndroid Build Coastguard Worker                                       const TArray<SpvId>& args, OutputStream& out);
322*c8dee2aaSAndroid Build Coastguard Worker 
323*c8dee2aaSAndroid Build Coastguard Worker     /**
324*c8dee2aaSAndroid Build Coastguard Worker      * Promotes an expression to a vector. If the expression is already a vector with vectorSize
325*c8dee2aaSAndroid Build Coastguard Worker      * columns, returns it unmodified. If the expression is a scalar, either promotes it to a
326*c8dee2aaSAndroid Build Coastguard Worker      * vector (if vectorSize > 1) or returns it unmodified (if vectorSize == 1). Asserts if the
327*c8dee2aaSAndroid Build Coastguard Worker      * expression is already a vector and it does not have vectorSize columns.
328*c8dee2aaSAndroid Build Coastguard Worker      */
329*c8dee2aaSAndroid Build Coastguard Worker     SpvId vectorize(const Expression& expr, int vectorSize, OutputStream& out);
330*c8dee2aaSAndroid Build Coastguard Worker 
331*c8dee2aaSAndroid Build Coastguard Worker     /**
332*c8dee2aaSAndroid Build Coastguard Worker      * Given a list of potentially mixed scalars and vectors, promotes the scalars to match the
333*c8dee2aaSAndroid Build Coastguard Worker      * size of the vectors and returns the ids of the written expressions. e.g. given (float, vec2),
334*c8dee2aaSAndroid Build Coastguard Worker      * returns (vec2(float), vec2). It is an error to use mismatched vector sizes, e.g. (float,
335*c8dee2aaSAndroid Build Coastguard Worker      * vec2, vec3).
336*c8dee2aaSAndroid Build Coastguard Worker      */
337*c8dee2aaSAndroid Build Coastguard Worker     TArray<SpvId> vectorize(const ExpressionArray& args, OutputStream& out);
338*c8dee2aaSAndroid Build Coastguard Worker 
339*c8dee2aaSAndroid Build Coastguard Worker     /**
340*c8dee2aaSAndroid Build Coastguard Worker      * Given a SpvId of a scalar, splats it across the passed-in type (scalar, vector or matrix) and
341*c8dee2aaSAndroid Build Coastguard Worker      * returns the SpvId of the new value.
342*c8dee2aaSAndroid Build Coastguard Worker      */
343*c8dee2aaSAndroid Build Coastguard Worker     SpvId splat(const Type& type, SpvId id, OutputStream& out);
344*c8dee2aaSAndroid Build Coastguard Worker 
345*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind, OutputStream& out);
346*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeAtomicIntrinsic(const FunctionCall& c,
347*c8dee2aaSAndroid Build Coastguard Worker                                SpecialIntrinsic kind,
348*c8dee2aaSAndroid Build Coastguard Worker                                SpvId resultId,
349*c8dee2aaSAndroid Build Coastguard Worker                                OutputStream& out);
350*c8dee2aaSAndroid Build Coastguard Worker 
351*c8dee2aaSAndroid Build Coastguard Worker     SpvId castScalarToFloat(SpvId inputId, const Type& inputType, const Type& outputType,
352*c8dee2aaSAndroid Build Coastguard Worker                             OutputStream& out);
353*c8dee2aaSAndroid Build Coastguard Worker 
354*c8dee2aaSAndroid Build Coastguard Worker     SpvId castScalarToSignedInt(SpvId inputId, const Type& inputType, const Type& outputType,
355*c8dee2aaSAndroid Build Coastguard Worker                                 OutputStream& out);
356*c8dee2aaSAndroid Build Coastguard Worker 
357*c8dee2aaSAndroid Build Coastguard Worker     SpvId castScalarToUnsignedInt(SpvId inputId, const Type& inputType, const Type& outputType,
358*c8dee2aaSAndroid Build Coastguard Worker                                   OutputStream& out);
359*c8dee2aaSAndroid Build Coastguard Worker 
360*c8dee2aaSAndroid Build Coastguard Worker     SpvId castScalarToBoolean(SpvId inputId, const Type& inputType, const Type& outputType,
361*c8dee2aaSAndroid Build Coastguard Worker                               OutputStream& out);
362*c8dee2aaSAndroid Build Coastguard Worker 
363*c8dee2aaSAndroid Build Coastguard Worker     SpvId castScalarToType(SpvId inputExprId, const Type& inputType, const Type& outputType,
364*c8dee2aaSAndroid Build Coastguard Worker                            OutputStream& out);
365*c8dee2aaSAndroid Build Coastguard Worker 
366*c8dee2aaSAndroid Build Coastguard Worker     /**
367*c8dee2aaSAndroid Build Coastguard Worker      * Writes a potentially-different-sized copy of a matrix. Entries which do not exist in the
368*c8dee2aaSAndroid Build Coastguard Worker      * source matrix are filled with zero; entries which do not exist in the destination matrix are
369*c8dee2aaSAndroid Build Coastguard Worker      * ignored.
370*c8dee2aaSAndroid Build Coastguard Worker      */
371*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeMatrixCopy(SpvId src, const Type& srcType, const Type& dstType, OutputStream& out);
372*c8dee2aaSAndroid Build Coastguard Worker 
373*c8dee2aaSAndroid Build Coastguard Worker     void addColumnEntry(const Type& columnType,
374*c8dee2aaSAndroid Build Coastguard Worker                         TArray<SpvId>* currentColumn,
375*c8dee2aaSAndroid Build Coastguard Worker                         TArray<SpvId>* columnIds,
376*c8dee2aaSAndroid Build Coastguard Worker                         int rows,
377*c8dee2aaSAndroid Build Coastguard Worker                         SpvId entry,
378*c8dee2aaSAndroid Build Coastguard Worker                         OutputStream& out);
379*c8dee2aaSAndroid Build Coastguard Worker 
380*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeConstructorCompound(const ConstructorCompound& c, OutputStream& out);
381*c8dee2aaSAndroid Build Coastguard Worker 
382*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeMatrixConstructor(const ConstructorCompound& c, OutputStream& out);
383*c8dee2aaSAndroid Build Coastguard Worker 
384*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeVectorConstructor(const ConstructorCompound& c, OutputStream& out);
385*c8dee2aaSAndroid Build Coastguard Worker 
386*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeCompositeConstructor(const AnyConstructor& c, OutputStream& out);
387*c8dee2aaSAndroid Build Coastguard Worker 
388*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c, OutputStream& out);
389*c8dee2aaSAndroid Build Coastguard Worker 
390*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeConstructorMatrixResize(const ConstructorMatrixResize& c, OutputStream& out);
391*c8dee2aaSAndroid Build Coastguard Worker 
392*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeConstructorScalarCast(const ConstructorScalarCast& c, OutputStream& out);
393*c8dee2aaSAndroid Build Coastguard Worker 
394*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeConstructorSplat(const ConstructorSplat& c, OutputStream& out);
395*c8dee2aaSAndroid Build Coastguard Worker 
396*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeConstructorCompoundCast(const ConstructorCompoundCast& c, OutputStream& out);
397*c8dee2aaSAndroid Build Coastguard Worker 
398*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeFieldAccess(const FieldAccess& f, OutputStream& out);
399*c8dee2aaSAndroid Build Coastguard Worker 
400*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeSwizzle(const Expression& baseExpr,
401*c8dee2aaSAndroid Build Coastguard Worker                        const ComponentArray& components,
402*c8dee2aaSAndroid Build Coastguard Worker                        OutputStream& out);
403*c8dee2aaSAndroid Build Coastguard Worker 
404*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeSwizzle(const Swizzle& swizzle, OutputStream& out);
405*c8dee2aaSAndroid Build Coastguard Worker 
406*c8dee2aaSAndroid Build Coastguard Worker     /**
407*c8dee2aaSAndroid Build Coastguard Worker      * Folds the potentially-vector result of a logical operation down to a single bool. If
408*c8dee2aaSAndroid Build Coastguard Worker      * operandType is a vector type, assumes that the intermediate result in id is a bvec of the
409*c8dee2aaSAndroid Build Coastguard Worker      * same dimensions, and applys all() to it to fold it down to a single bool value. Otherwise,
410*c8dee2aaSAndroid Build Coastguard Worker      * returns the original id value.
411*c8dee2aaSAndroid Build Coastguard Worker      */
412*c8dee2aaSAndroid Build Coastguard Worker     SpvId foldToBool(SpvId id, const Type& operandType, SpvOp op, OutputStream& out);
413*c8dee2aaSAndroid Build Coastguard Worker 
414*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeMatrixComparison(const Type& operandType, SpvId lhs, SpvId rhs, SpvOp_ floatOperator,
415*c8dee2aaSAndroid Build Coastguard Worker                                 SpvOp_ intOperator, SpvOp_ vectorMergeOperator,
416*c8dee2aaSAndroid Build Coastguard Worker                                 SpvOp_ mergeOperator, OutputStream& out);
417*c8dee2aaSAndroid Build Coastguard Worker 
418*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeStructComparison(const Type& structType, SpvId lhs, Operator op, SpvId rhs,
419*c8dee2aaSAndroid Build Coastguard Worker                                 OutputStream& out);
420*c8dee2aaSAndroid Build Coastguard Worker 
421*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeArrayComparison(const Type& structType, SpvId lhs, Operator op, SpvId rhs,
422*c8dee2aaSAndroid Build Coastguard Worker                                OutputStream& out);
423*c8dee2aaSAndroid Build Coastguard Worker 
424*c8dee2aaSAndroid Build Coastguard Worker     // Used by writeStructComparison and writeArrayComparison to logically combine field-by-field
425*c8dee2aaSAndroid Build Coastguard Worker     // comparisons into an overall comparison result.
426*c8dee2aaSAndroid Build Coastguard Worker     // - `a.x == b.x` merged with `a.y == b.y` generates `(a.x == b.x) && (a.y == b.y)`
427*c8dee2aaSAndroid Build Coastguard Worker     // - `a.x != b.x` merged with `a.y != b.y` generates `(a.x != b.x) || (a.y != b.y)`
428*c8dee2aaSAndroid Build Coastguard Worker     SpvId mergeComparisons(SpvId comparison, SpvId allComparisons, Operator op, OutputStream& out);
429*c8dee2aaSAndroid Build Coastguard Worker 
430*c8dee2aaSAndroid Build Coastguard Worker     // When the RewriteMatrixVectorMultiply caps bit is set, we manually decompose the M*V
431*c8dee2aaSAndroid Build Coastguard Worker     // multiplication into a sum of vector-scalar products.
432*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeDecomposedMatrixVectorMultiply(const Type& leftType,
433*c8dee2aaSAndroid Build Coastguard Worker                                               SpvId lhs,
434*c8dee2aaSAndroid Build Coastguard Worker                                               const Type& rightType,
435*c8dee2aaSAndroid Build Coastguard Worker                                               SpvId rhs,
436*c8dee2aaSAndroid Build Coastguard Worker                                               const Type& resultType,
437*c8dee2aaSAndroid Build Coastguard Worker                                               OutputStream& out);
438*c8dee2aaSAndroid Build Coastguard Worker 
439*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeComponentwiseMatrixUnary(const Type& operandType,
440*c8dee2aaSAndroid Build Coastguard Worker                                         SpvId operand,
441*c8dee2aaSAndroid Build Coastguard Worker                                         SpvOp_ op,
442*c8dee2aaSAndroid Build Coastguard Worker                                         OutputStream& out);
443*c8dee2aaSAndroid Build Coastguard Worker 
444*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeComponentwiseMatrixBinary(const Type& operandType, SpvId lhs, SpvId rhs,
445*c8dee2aaSAndroid Build Coastguard Worker                                          SpvOp_ op, OutputStream& out);
446*c8dee2aaSAndroid Build Coastguard Worker 
447*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeBinaryOperationComponentwiseIfMatrix(const Type& resultType, const Type& operandType,
448*c8dee2aaSAndroid Build Coastguard Worker                                                     SpvId lhs, SpvId rhs,
449*c8dee2aaSAndroid Build Coastguard Worker                                                     SpvOp_ ifFloat, SpvOp_ ifInt,
450*c8dee2aaSAndroid Build Coastguard Worker                                                     SpvOp_ ifUInt, SpvOp_ ifBool,
451*c8dee2aaSAndroid Build Coastguard Worker                                                     OutputStream& out);
452*c8dee2aaSAndroid Build Coastguard Worker 
453*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs,
454*c8dee2aaSAndroid Build Coastguard Worker                                SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt,
455*c8dee2aaSAndroid Build Coastguard Worker                                SpvOp_ ifBool, OutputStream& out);
456*c8dee2aaSAndroid Build Coastguard Worker 
457*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeBinaryOperation(const Type& resultType, const Type& operandType, SpvId lhs,
458*c8dee2aaSAndroid Build Coastguard Worker                                SpvId rhs, bool writeComponentwiseIfMatrix, SpvOp_ ifFloat,
459*c8dee2aaSAndroid Build Coastguard Worker                                SpvOp_ ifInt, SpvOp_ ifUInt, SpvOp_ ifBool, OutputStream& out);
460*c8dee2aaSAndroid Build Coastguard Worker 
461*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeReciprocal(const Type& type, SpvId value, OutputStream& out);
462*c8dee2aaSAndroid Build Coastguard Worker 
463*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeBinaryExpression(const Type& leftType, SpvId lhs, Operator op,
464*c8dee2aaSAndroid Build Coastguard Worker                                 const Type& rightType, SpvId rhs, const Type& resultType,
465*c8dee2aaSAndroid Build Coastguard Worker                                 OutputStream& out);
466*c8dee2aaSAndroid Build Coastguard Worker 
467*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeBinaryExpression(const BinaryExpression& b, OutputStream& out);
468*c8dee2aaSAndroid Build Coastguard Worker 
469*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeTernaryExpression(const TernaryExpression& t, OutputStream& out);
470*c8dee2aaSAndroid Build Coastguard Worker 
471*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeIndexExpression(const IndexExpression& expr, OutputStream& out);
472*c8dee2aaSAndroid Build Coastguard Worker 
473*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeLogicalAnd(const Expression& left, const Expression& right, OutputStream& out);
474*c8dee2aaSAndroid Build Coastguard Worker 
475*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeLogicalOr(const Expression& left, const Expression& right, OutputStream& out);
476*c8dee2aaSAndroid Build Coastguard Worker 
477*c8dee2aaSAndroid Build Coastguard Worker     SpvId writePrefixExpression(const PrefixExpression& p, OutputStream& out);
478*c8dee2aaSAndroid Build Coastguard Worker 
479*c8dee2aaSAndroid Build Coastguard Worker     SpvId writePostfixExpression(const PostfixExpression& p, OutputStream& out);
480*c8dee2aaSAndroid Build Coastguard Worker 
481*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeLiteral(const Literal& f);
482*c8dee2aaSAndroid Build Coastguard Worker 
483*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeLiteral(double value, const Type& type);
484*c8dee2aaSAndroid Build Coastguard Worker 
485*c8dee2aaSAndroid Build Coastguard Worker     void writeStatement(const Statement& s, OutputStream& out);
486*c8dee2aaSAndroid Build Coastguard Worker 
487*c8dee2aaSAndroid Build Coastguard Worker     void writeBlock(const Block& b, OutputStream& out);
488*c8dee2aaSAndroid Build Coastguard Worker 
489*c8dee2aaSAndroid Build Coastguard Worker     void writeIfStatement(const IfStatement& stmt, OutputStream& out);
490*c8dee2aaSAndroid Build Coastguard Worker 
491*c8dee2aaSAndroid Build Coastguard Worker     void writeForStatement(const ForStatement& f, OutputStream& out);
492*c8dee2aaSAndroid Build Coastguard Worker 
493*c8dee2aaSAndroid Build Coastguard Worker     void writeDoStatement(const DoStatement& d, OutputStream& out);
494*c8dee2aaSAndroid Build Coastguard Worker 
495*c8dee2aaSAndroid Build Coastguard Worker     void writeSwitchStatement(const SwitchStatement& s, OutputStream& out);
496*c8dee2aaSAndroid Build Coastguard Worker 
497*c8dee2aaSAndroid Build Coastguard Worker     void writeReturnStatement(const ReturnStatement& r, OutputStream& out);
498*c8dee2aaSAndroid Build Coastguard Worker 
499*c8dee2aaSAndroid Build Coastguard Worker     void writeCapabilities(OutputStream& out);
500*c8dee2aaSAndroid Build Coastguard Worker 
501*c8dee2aaSAndroid Build Coastguard Worker     void writeInstructions(const Program& program, OutputStream& out);
502*c8dee2aaSAndroid Build Coastguard Worker 
503*c8dee2aaSAndroid Build Coastguard Worker     void writeOpCode(SpvOp_ opCode, int length, OutputStream& out);
504*c8dee2aaSAndroid Build Coastguard Worker 
505*c8dee2aaSAndroid Build Coastguard Worker     void writeWord(int32_t word, OutputStream& out);
506*c8dee2aaSAndroid Build Coastguard Worker 
507*c8dee2aaSAndroid Build Coastguard Worker     void writeString(std::string_view s, OutputStream& out);
508*c8dee2aaSAndroid Build Coastguard Worker 
509*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, OutputStream& out);
510*c8dee2aaSAndroid Build Coastguard Worker 
511*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, std::string_view string, OutputStream& out);
512*c8dee2aaSAndroid Build Coastguard Worker 
513*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, int32_t word1, OutputStream& out);
514*c8dee2aaSAndroid Build Coastguard Worker 
515*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, int32_t word1, std::string_view string,
516*c8dee2aaSAndroid Build Coastguard Worker                           OutputStream& out);
517*c8dee2aaSAndroid Build Coastguard Worker 
518*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, std::string_view string,
519*c8dee2aaSAndroid Build Coastguard Worker                           OutputStream& out);
520*c8dee2aaSAndroid Build Coastguard Worker 
521*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, OutputStream& out);
522*c8dee2aaSAndroid Build Coastguard Worker 
523*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3,
524*c8dee2aaSAndroid Build Coastguard Worker                           OutputStream& out);
525*c8dee2aaSAndroid Build Coastguard Worker 
526*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
527*c8dee2aaSAndroid Build Coastguard Worker                           OutputStream& out);
528*c8dee2aaSAndroid Build Coastguard Worker 
529*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
530*c8dee2aaSAndroid Build Coastguard Worker                           int32_t word5, OutputStream& out);
531*c8dee2aaSAndroid Build Coastguard Worker 
532*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
533*c8dee2aaSAndroid Build Coastguard Worker                           int32_t word5, int32_t word6, OutputStream& out);
534*c8dee2aaSAndroid Build Coastguard Worker 
535*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
536*c8dee2aaSAndroid Build Coastguard Worker                           int32_t word5, int32_t word6, int32_t word7, OutputStream& out);
537*c8dee2aaSAndroid Build Coastguard Worker 
538*c8dee2aaSAndroid Build Coastguard Worker     void writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2, int32_t word3, int32_t word4,
539*c8dee2aaSAndroid Build Coastguard Worker                           int32_t word5, int32_t word6, int32_t word7, int32_t word8,
540*c8dee2aaSAndroid Build Coastguard Worker                           OutputStream& out);
541*c8dee2aaSAndroid Build Coastguard Worker 
542*c8dee2aaSAndroid Build Coastguard Worker     // This form of writeInstruction can deduplicate redundant ops.
543*c8dee2aaSAndroid Build Coastguard Worker     struct Word;
544*c8dee2aaSAndroid Build Coastguard Worker     // 8 Words is enough for nearly all instructions (except variable-length instructions like
545*c8dee2aaSAndroid Build Coastguard Worker     // OpAccessChain or OpConstantComposite).
546*c8dee2aaSAndroid Build Coastguard Worker     using Words = STArray<8, Word, true>;
547*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeInstruction(SpvOp_ opCode, const TArray<Word, true>& words, OutputStream& out);
548*c8dee2aaSAndroid Build Coastguard Worker 
549*c8dee2aaSAndroid Build Coastguard Worker     struct Instruction {
550*c8dee2aaSAndroid Build Coastguard Worker         SpvId fOp;
551*c8dee2aaSAndroid Build Coastguard Worker         int32_t fResultKind;
552*c8dee2aaSAndroid Build Coastguard Worker         STArray<8, int32_t>  fWords;
553*c8dee2aaSAndroid Build Coastguard Worker 
554*c8dee2aaSAndroid Build Coastguard Worker         bool operator==(const Instruction& that) const;
555*c8dee2aaSAndroid Build Coastguard Worker         struct Hash;
556*c8dee2aaSAndroid Build Coastguard Worker     };
557*c8dee2aaSAndroid Build Coastguard Worker 
558*c8dee2aaSAndroid Build Coastguard Worker     static Instruction BuildInstructionKey(SpvOp_ opCode, const TArray<Word, true>& words);
559*c8dee2aaSAndroid Build Coastguard Worker 
560*c8dee2aaSAndroid Build Coastguard Worker     // The writeOpXxxxx calls will simplify and deduplicate ops where possible.
561*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeOpConstantTrue(const Type& type);
562*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeOpConstantFalse(const Type& type);
563*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeOpConstant(const Type& type, int32_t valueBits);
564*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeOpConstantComposite(const Type& type, const TArray<SpvId>& values);
565*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeOpCompositeConstruct(const Type& type, const TArray<SpvId>&, OutputStream& out);
566*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeOpCompositeExtract(const Type& type, SpvId base, int component, OutputStream& out);
567*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeOpCompositeExtract(const Type& type, SpvId base, int componentA, int componentB,
568*c8dee2aaSAndroid Build Coastguard Worker                                   OutputStream& out);
569*c8dee2aaSAndroid Build Coastguard Worker     SpvId writeOpLoad(SpvId type, Precision precision, SpvId pointer, OutputStream& out);
570*c8dee2aaSAndroid Build Coastguard Worker     void writeOpStore(StorageClass storageClass, SpvId pointer, SpvId value, OutputStream& out);
571*c8dee2aaSAndroid Build Coastguard Worker 
572*c8dee2aaSAndroid Build Coastguard Worker     // Converts the provided SpvId(s) into an array of scalar OpConstants, if it can be done.
573*c8dee2aaSAndroid Build Coastguard Worker     bool toConstants(SpvId value, TArray<SpvId>* constants);
574*c8dee2aaSAndroid Build Coastguard Worker     bool toConstants(SkSpan<const SpvId> values, TArray<SpvId>* constants);
575*c8dee2aaSAndroid Build Coastguard Worker 
576*c8dee2aaSAndroid Build Coastguard Worker     // Extracts the requested component SpvId from a composite instruction, if it can be done.
577*c8dee2aaSAndroid Build Coastguard Worker     Instruction* resultTypeForInstruction(const Instruction& instr);
578*c8dee2aaSAndroid Build Coastguard Worker     int numComponentsForVecInstruction(const Instruction& instr);
579*c8dee2aaSAndroid Build Coastguard Worker     SpvId toComponent(SpvId id, int component);
580*c8dee2aaSAndroid Build Coastguard Worker 
581*c8dee2aaSAndroid Build Coastguard Worker     struct ConditionalOpCounts {
582*c8dee2aaSAndroid Build Coastguard Worker         int numReachableOps;
583*c8dee2aaSAndroid Build Coastguard Worker         int numStoreOps;
584*c8dee2aaSAndroid Build Coastguard Worker     };
585*c8dee2aaSAndroid Build Coastguard Worker     ConditionalOpCounts getConditionalOpCounts();
586*c8dee2aaSAndroid Build Coastguard Worker     void pruneConditionalOps(ConditionalOpCounts ops);
587*c8dee2aaSAndroid Build Coastguard Worker 
588*c8dee2aaSAndroid Build Coastguard Worker     enum StraightLineLabelType {
589*c8dee2aaSAndroid Build Coastguard Worker         // Use "BranchlessBlock" for blocks which are never explicitly branched-to at all. This
590*c8dee2aaSAndroid Build Coastguard Worker         // happens at the start of a function, or when we find unreachable code.
591*c8dee2aaSAndroid Build Coastguard Worker         kBranchlessBlock,
592*c8dee2aaSAndroid Build Coastguard Worker 
593*c8dee2aaSAndroid Build Coastguard Worker         // Use "BranchIsOnPreviousLine" when writing a label that comes immediately after its
594*c8dee2aaSAndroid Build Coastguard Worker         // associated branch. Example usage:
595*c8dee2aaSAndroid Build Coastguard Worker         // - SPIR-V does not implicitly fall through from one block to the next, so you may need to
596*c8dee2aaSAndroid Build Coastguard Worker         //   use an OpBranch to explicitly jump to the next block, even when they are adjacent in
597*c8dee2aaSAndroid Build Coastguard Worker         //   the code.
598*c8dee2aaSAndroid Build Coastguard Worker         // - The block immediately following an OpBranchConditional or OpSwitch.
599*c8dee2aaSAndroid Build Coastguard Worker         kBranchIsOnPreviousLine,
600*c8dee2aaSAndroid Build Coastguard Worker     };
601*c8dee2aaSAndroid Build Coastguard Worker 
602*c8dee2aaSAndroid Build Coastguard Worker     enum BranchingLabelType {
603*c8dee2aaSAndroid Build Coastguard Worker         // Use "BranchIsAbove" for labels which are referenced by OpBranch or OpBranchConditional
604*c8dee2aaSAndroid Build Coastguard Worker         // ops that are above the label in the code--i.e., the branch skips forward in the code.
605*c8dee2aaSAndroid Build Coastguard Worker         kBranchIsAbove,
606*c8dee2aaSAndroid Build Coastguard Worker 
607*c8dee2aaSAndroid Build Coastguard Worker         // Use "BranchIsBelow" for labels which are referenced by OpBranch or OpBranchConditional
608*c8dee2aaSAndroid Build Coastguard Worker         // ops below the label in the code--i.e., the branch jumps backward in the code.
609*c8dee2aaSAndroid Build Coastguard Worker         kBranchIsBelow,
610*c8dee2aaSAndroid Build Coastguard Worker 
611*c8dee2aaSAndroid Build Coastguard Worker         // Use "BranchesOnBothSides" for labels which have branches coming from both directions.
612*c8dee2aaSAndroid Build Coastguard Worker         kBranchesOnBothSides,
613*c8dee2aaSAndroid Build Coastguard Worker     };
614*c8dee2aaSAndroid Build Coastguard Worker     void writeLabel(SpvId label, StraightLineLabelType type, OutputStream& out);
615*c8dee2aaSAndroid Build Coastguard Worker     void writeLabel(SpvId label, BranchingLabelType type, ConditionalOpCounts ops,
616*c8dee2aaSAndroid Build Coastguard Worker                     OutputStream& out);
617*c8dee2aaSAndroid Build Coastguard Worker 
618*c8dee2aaSAndroid Build Coastguard Worker     MemoryLayout memoryLayoutForStorageClass(StorageClass storageClass);
619*c8dee2aaSAndroid Build Coastguard Worker     MemoryLayout memoryLayoutForVariable(const Variable&) const;
620*c8dee2aaSAndroid Build Coastguard Worker 
621*c8dee2aaSAndroid Build Coastguard Worker     struct EntrypointAdapter {
622*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<FunctionDefinition> entrypointDef;
623*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<FunctionDeclaration> entrypointDecl;
624*c8dee2aaSAndroid Build Coastguard Worker     };
625*c8dee2aaSAndroid Build Coastguard Worker 
626*c8dee2aaSAndroid Build Coastguard Worker     EntrypointAdapter writeEntrypointAdapter(const FunctionDeclaration& main);
627*c8dee2aaSAndroid Build Coastguard Worker 
628*c8dee2aaSAndroid Build Coastguard Worker     struct UniformBuffer {
629*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<InterfaceBlock> fInterfaceBlock;
630*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<Variable> fInnerVariable;
631*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<Type> fStruct;
632*c8dee2aaSAndroid Build Coastguard Worker     };
633*c8dee2aaSAndroid Build Coastguard Worker 
634*c8dee2aaSAndroid Build Coastguard Worker     void writeUniformBuffer(SymbolTable* topLevelSymbolTable);
635*c8dee2aaSAndroid Build Coastguard Worker 
636*c8dee2aaSAndroid Build Coastguard Worker     void addRTFlipUniform(Position pos);
637*c8dee2aaSAndroid Build Coastguard Worker 
638*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Expression> identifier(std::string_view name);
639*c8dee2aaSAndroid Build Coastguard Worker 
640*c8dee2aaSAndroid Build Coastguard Worker     std::tuple<const Variable*, const Variable*> synthesizeTextureAndSampler(
641*c8dee2aaSAndroid Build Coastguard Worker             const Variable& combinedSampler);
642*c8dee2aaSAndroid Build Coastguard Worker 
643*c8dee2aaSAndroid Build Coastguard Worker     const MemoryLayout fDefaultMemoryLayout{MemoryLayout::Standard::k140};
644*c8dee2aaSAndroid Build Coastguard Worker 
645*c8dee2aaSAndroid Build Coastguard Worker     uint64_t fCapabilities = 0;
646*c8dee2aaSAndroid Build Coastguard Worker     SpvId fIdCount = 1;
647*c8dee2aaSAndroid Build Coastguard Worker     SpvId fGLSLExtendedInstructions;
648*c8dee2aaSAndroid Build Coastguard Worker     struct Intrinsic {
649*c8dee2aaSAndroid Build Coastguard Worker         IntrinsicOpcodeKind opKind;
650*c8dee2aaSAndroid Build Coastguard Worker         int32_t floatOp;
651*c8dee2aaSAndroid Build Coastguard Worker         int32_t signedOp;
652*c8dee2aaSAndroid Build Coastguard Worker         int32_t unsignedOp;
653*c8dee2aaSAndroid Build Coastguard Worker         int32_t boolOp;
654*c8dee2aaSAndroid Build Coastguard Worker     };
655*c8dee2aaSAndroid Build Coastguard Worker     Intrinsic getIntrinsic(IntrinsicKind) const;
656*c8dee2aaSAndroid Build Coastguard Worker 
657*c8dee2aaSAndroid Build Coastguard Worker     THashMap<Analysis::SpecializedFunctionKey, SpvId, Analysis::SpecializedFunctionKey::Hash>
658*c8dee2aaSAndroid Build Coastguard Worker             fFunctionMap;
659*c8dee2aaSAndroid Build Coastguard Worker 
660*c8dee2aaSAndroid Build Coastguard Worker     Analysis::SpecializationInfo fSpecializationInfo;
661*c8dee2aaSAndroid Build Coastguard Worker     Analysis::SpecializationIndex fActiveSpecializationIndex = Analysis::kUnspecialized;
662*c8dee2aaSAndroid Build Coastguard Worker     const Analysis::SpecializedParameters* fActiveSpecialization = nullptr;
663*c8dee2aaSAndroid Build Coastguard Worker 
664*c8dee2aaSAndroid Build Coastguard Worker     THashMap<const Variable*, SpvId> fVariableMap;
665*c8dee2aaSAndroid Build Coastguard Worker     THashMap<const Type*, SpvId> fStructMap;
666*c8dee2aaSAndroid Build Coastguard Worker     StringStream fGlobalInitializersBuffer;
667*c8dee2aaSAndroid Build Coastguard Worker     StringStream fConstantBuffer;
668*c8dee2aaSAndroid Build Coastguard Worker     StringStream fVariableBuffer;
669*c8dee2aaSAndroid Build Coastguard Worker     StringStream fNameBuffer;
670*c8dee2aaSAndroid Build Coastguard Worker     StringStream fDecorationBuffer;
671*c8dee2aaSAndroid Build Coastguard Worker 
672*c8dee2aaSAndroid Build Coastguard Worker     // Mapping from combined sampler declarations to synthesized texture/sampler variables.
673*c8dee2aaSAndroid Build Coastguard Worker     // This is used when the sampler is declared as `layout(webgpu)` or `layout(direct3d)`.
674*c8dee2aaSAndroid Build Coastguard Worker     bool fUseTextureSamplerPairs = false;
675*c8dee2aaSAndroid Build Coastguard Worker     struct SynthesizedTextureSamplerPair {
676*c8dee2aaSAndroid Build Coastguard Worker         // The names of the synthesized variables. The Variable objects themselves store string
677*c8dee2aaSAndroid Build Coastguard Worker         // views referencing these strings. It is important for the std::string instances to have a
678*c8dee2aaSAndroid Build Coastguard Worker         // fixed memory location after the string views get created, which is why
679*c8dee2aaSAndroid Build Coastguard Worker         // `fSynthesizedSamplerMap` stores unique_ptr instead of values.
680*c8dee2aaSAndroid Build Coastguard Worker         std::string fTextureName;
681*c8dee2aaSAndroid Build Coastguard Worker         std::string fSamplerName;
682*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<Variable> fTexture;
683*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<Variable> fSampler;
684*c8dee2aaSAndroid Build Coastguard Worker     };
685*c8dee2aaSAndroid Build Coastguard Worker     THashMap<const Variable*, std::unique_ptr<SynthesizedTextureSamplerPair>>
686*c8dee2aaSAndroid Build Coastguard Worker             fSynthesizedSamplerMap;
687*c8dee2aaSAndroid Build Coastguard Worker 
688*c8dee2aaSAndroid Build Coastguard Worker     // These caches map SpvIds to Instructions, and vice-versa. This enables us to deduplicate code
689*c8dee2aaSAndroid Build Coastguard Worker     // (by detecting an Instruction we've already issued and reusing the SpvId), and to introspect
690*c8dee2aaSAndroid Build Coastguard Worker     // and simplify code we've already emitted  (by taking a SpvId from an Instruction and following
691*c8dee2aaSAndroid Build Coastguard Worker     // it back to its source).
692*c8dee2aaSAndroid Build Coastguard Worker 
693*c8dee2aaSAndroid Build Coastguard Worker     // A map of instruction -> SpvId:
694*c8dee2aaSAndroid Build Coastguard Worker     THashMap<Instruction, SpvId, Instruction::Hash> fOpCache;
695*c8dee2aaSAndroid Build Coastguard Worker     // A map of SpvId -> instruction:
696*c8dee2aaSAndroid Build Coastguard Worker     THashMap<SpvId, Instruction> fSpvIdCache;
697*c8dee2aaSAndroid Build Coastguard Worker     // A map of SpvId -> value SpvId:
698*c8dee2aaSAndroid Build Coastguard Worker     THashMap<SpvId, SpvId> fStoreCache;
699*c8dee2aaSAndroid Build Coastguard Worker 
700*c8dee2aaSAndroid Build Coastguard Worker     // "Reachable" ops are instructions which can safely be accessed from the current block.
701*c8dee2aaSAndroid Build Coastguard Worker     // For instance, if our SPIR-V contains `%3 = OpFAdd %1 %2`, we would be able to access and
702*c8dee2aaSAndroid Build Coastguard Worker     // reuse that computation on following lines. However, if that Add operation occurred inside an
703*c8dee2aaSAndroid Build Coastguard Worker     // `if` block, then its SpvId becomes inaccessible once we complete the if statement (since
704*c8dee2aaSAndroid Build Coastguard Worker     // depending on the if condition, we may or may not have actually done that computation). The
705*c8dee2aaSAndroid Build Coastguard Worker     // same logic applies to other control-flow blocks as well. Once an instruction becomes
706*c8dee2aaSAndroid Build Coastguard Worker     // unreachable, we remove it from both op-caches.
707*c8dee2aaSAndroid Build Coastguard Worker     TArray<SpvId> fReachableOps;
708*c8dee2aaSAndroid Build Coastguard Worker 
709*c8dee2aaSAndroid Build Coastguard Worker     // The "store-ops" list contains a running list of all the pointers in the store cache. If a
710*c8dee2aaSAndroid Build Coastguard Worker     // store occurs inside of a conditional block, once that block exits, we no longer know what is
711*c8dee2aaSAndroid Build Coastguard Worker     // stored in that particular SpvId. At that point, we must remove any associated entry from the
712*c8dee2aaSAndroid Build Coastguard Worker     // store cache.
713*c8dee2aaSAndroid Build Coastguard Worker     TArray<SpvId> fStoreOps;
714*c8dee2aaSAndroid Build Coastguard Worker 
715*c8dee2aaSAndroid Build Coastguard Worker     // label of the current block, or 0 if we are not in a block
716*c8dee2aaSAndroid Build Coastguard Worker     SpvId fCurrentBlock = 0;
717*c8dee2aaSAndroid Build Coastguard Worker     TArray<SpvId> fBreakTarget;
718*c8dee2aaSAndroid Build Coastguard Worker     TArray<SpvId> fContinueTarget;
719*c8dee2aaSAndroid Build Coastguard Worker     bool fWroteRTFlip = false;
720*c8dee2aaSAndroid Build Coastguard Worker     // holds variables synthesized during output, for lifetime purposes
721*c8dee2aaSAndroid Build Coastguard Worker     SymbolTable fSynthetics{/*builtin=*/true};
722*c8dee2aaSAndroid Build Coastguard Worker     // Holds a list of uniforms that were declared as globals at the top-level instead of in an
723*c8dee2aaSAndroid Build Coastguard Worker     // interface block.
724*c8dee2aaSAndroid Build Coastguard Worker     UniformBuffer fUniformBuffer;
725*c8dee2aaSAndroid Build Coastguard Worker     std::vector<const VarDeclaration*> fTopLevelUniforms;
726*c8dee2aaSAndroid Build Coastguard Worker     THashMap<const Variable*, int> fTopLevelUniformMap;  // <var, UniformBuffer field index>
727*c8dee2aaSAndroid Build Coastguard Worker     SpvId fUniformBufferId = NA;
728*c8dee2aaSAndroid Build Coastguard Worker 
729*c8dee2aaSAndroid Build Coastguard Worker     friend class PointerLValue;
730*c8dee2aaSAndroid Build Coastguard Worker     friend class SwizzleLValue;
731*c8dee2aaSAndroid Build Coastguard Worker };
732*c8dee2aaSAndroid Build Coastguard Worker 
733*c8dee2aaSAndroid Build Coastguard Worker // Equality and hash operators for Instructions.
operator ==(const SPIRVCodeGenerator::Instruction & that) const734*c8dee2aaSAndroid Build Coastguard Worker bool SPIRVCodeGenerator::Instruction::operator==(const SPIRVCodeGenerator::Instruction& that) const {
735*c8dee2aaSAndroid Build Coastguard Worker     return fOp         == that.fOp &&
736*c8dee2aaSAndroid Build Coastguard Worker            fResultKind == that.fResultKind &&
737*c8dee2aaSAndroid Build Coastguard Worker            fWords      == that.fWords;
738*c8dee2aaSAndroid Build Coastguard Worker }
739*c8dee2aaSAndroid Build Coastguard Worker 
740*c8dee2aaSAndroid Build Coastguard Worker struct SPIRVCodeGenerator::Instruction::Hash {
operator ()SkSL::SPIRVCodeGenerator::Instruction::Hash741*c8dee2aaSAndroid Build Coastguard Worker     uint32_t operator()(const SPIRVCodeGenerator::Instruction& key) const {
742*c8dee2aaSAndroid Build Coastguard Worker         uint32_t hash = key.fResultKind;
743*c8dee2aaSAndroid Build Coastguard Worker         hash = SkChecksum::Hash32(&key.fOp, sizeof(key.fOp), hash);
744*c8dee2aaSAndroid Build Coastguard Worker         hash = SkChecksum::Hash32(key.fWords.data(), key.fWords.size() * sizeof(int32_t), hash);
745*c8dee2aaSAndroid Build Coastguard Worker         return hash;
746*c8dee2aaSAndroid Build Coastguard Worker     }
747*c8dee2aaSAndroid Build Coastguard Worker };
748*c8dee2aaSAndroid Build Coastguard Worker 
749*c8dee2aaSAndroid Build Coastguard Worker // This class is used to pass values and result placeholder slots to writeInstruction.
750*c8dee2aaSAndroid Build Coastguard Worker struct SPIRVCodeGenerator::Word {
751*c8dee2aaSAndroid Build Coastguard Worker     enum Kind {
752*c8dee2aaSAndroid Build Coastguard Worker         kNone,  // intended for use as a sentinel, not part of any Instruction
753*c8dee2aaSAndroid Build Coastguard Worker         kSpvId,
754*c8dee2aaSAndroid Build Coastguard Worker         kNumber,
755*c8dee2aaSAndroid Build Coastguard Worker         kDefaultPrecisionResult,
756*c8dee2aaSAndroid Build Coastguard Worker         kRelaxedPrecisionResult,
757*c8dee2aaSAndroid Build Coastguard Worker         kUniqueResult,
758*c8dee2aaSAndroid Build Coastguard Worker         kKeyedResult,
759*c8dee2aaSAndroid Build Coastguard Worker     };
760*c8dee2aaSAndroid Build Coastguard Worker 
WordSkSL::SPIRVCodeGenerator::Word761*c8dee2aaSAndroid Build Coastguard Worker     Word(SpvId id) : fValue(id), fKind(Kind::kSpvId) {}
WordSkSL::SPIRVCodeGenerator::Word762*c8dee2aaSAndroid Build Coastguard Worker     Word(int32_t val, Kind kind) : fValue(val), fKind(kind) {}
763*c8dee2aaSAndroid Build Coastguard Worker 
NumberSkSL::SPIRVCodeGenerator::Word764*c8dee2aaSAndroid Build Coastguard Worker     static Word Number(int32_t val) {
765*c8dee2aaSAndroid Build Coastguard Worker         return Word{val, Kind::kNumber};
766*c8dee2aaSAndroid Build Coastguard Worker     }
767*c8dee2aaSAndroid Build Coastguard Worker 
ResultSkSL::SPIRVCodeGenerator::Word768*c8dee2aaSAndroid Build Coastguard Worker     static Word Result(const Type& type) {
769*c8dee2aaSAndroid Build Coastguard Worker         return (type.hasPrecision() && !type.highPrecision()) ? RelaxedResult() : Result();
770*c8dee2aaSAndroid Build Coastguard Worker     }
771*c8dee2aaSAndroid Build Coastguard Worker 
RelaxedResultSkSL::SPIRVCodeGenerator::Word772*c8dee2aaSAndroid Build Coastguard Worker     static Word RelaxedResult() {
773*c8dee2aaSAndroid Build Coastguard Worker         return Word{(int32_t)NA, kRelaxedPrecisionResult};
774*c8dee2aaSAndroid Build Coastguard Worker     }
775*c8dee2aaSAndroid Build Coastguard Worker 
UniqueResultSkSL::SPIRVCodeGenerator::Word776*c8dee2aaSAndroid Build Coastguard Worker     static Word UniqueResult() {
777*c8dee2aaSAndroid Build Coastguard Worker         return Word{(int32_t)NA, kUniqueResult};
778*c8dee2aaSAndroid Build Coastguard Worker     }
779*c8dee2aaSAndroid Build Coastguard Worker 
ResultSkSL::SPIRVCodeGenerator::Word780*c8dee2aaSAndroid Build Coastguard Worker     static Word Result() {
781*c8dee2aaSAndroid Build Coastguard Worker         return Word{(int32_t)NA, kDefaultPrecisionResult};
782*c8dee2aaSAndroid Build Coastguard Worker     }
783*c8dee2aaSAndroid Build Coastguard Worker 
784*c8dee2aaSAndroid Build Coastguard Worker     // Unlike a Result (where the result ID is always deduplicated to its first instruction) or a
785*c8dee2aaSAndroid Build Coastguard Worker     // UniqueResult (which always produces a new instruction), a KeyedResult allows an instruction
786*c8dee2aaSAndroid Build Coastguard Worker     // to be deduplicated among those that share the same `key`.
KeyedResultSkSL::SPIRVCodeGenerator::Word787*c8dee2aaSAndroid Build Coastguard Worker     static Word KeyedResult(int32_t key) { return Word{key, Kind::kKeyedResult}; }
788*c8dee2aaSAndroid Build Coastguard Worker 
isResultSkSL::SPIRVCodeGenerator::Word789*c8dee2aaSAndroid Build Coastguard Worker     bool isResult() const { return fKind >= Kind::kDefaultPrecisionResult; }
790*c8dee2aaSAndroid Build Coastguard Worker 
791*c8dee2aaSAndroid Build Coastguard Worker     int32_t fValue;
792*c8dee2aaSAndroid Build Coastguard Worker     Kind fKind;
793*c8dee2aaSAndroid Build Coastguard Worker };
794*c8dee2aaSAndroid Build Coastguard Worker 
795*c8dee2aaSAndroid Build Coastguard Worker // Skia's magic number is 31 and goes in the top 16 bits. We can use the lower bits to version the
796*c8dee2aaSAndroid Build Coastguard Worker // sksl generator if we want.
797*c8dee2aaSAndroid Build Coastguard Worker // https://github.com/KhronosGroup/SPIRV-Headers/blob/master/include/spirv/spir-v.xml#L84
798*c8dee2aaSAndroid Build Coastguard Worker static const int32_t SKSL_MAGIC  = 0x001F0000;
799*c8dee2aaSAndroid Build Coastguard Worker 
getIntrinsic(IntrinsicKind ik) const800*c8dee2aaSAndroid Build Coastguard Worker SPIRVCodeGenerator::Intrinsic SPIRVCodeGenerator::getIntrinsic(IntrinsicKind ik) const {
801*c8dee2aaSAndroid Build Coastguard Worker 
802*c8dee2aaSAndroid Build Coastguard Worker #define ALL_GLSL(x) Intrinsic{kGLSL_STD_450_IntrinsicOpcodeKind, GLSLstd450 ## x, \
803*c8dee2aaSAndroid Build Coastguard Worker                               GLSLstd450 ## x, GLSLstd450 ## x, GLSLstd450 ## x}
804*c8dee2aaSAndroid Build Coastguard Worker #define BY_TYPE_GLSL(ifFloat, ifInt, ifUInt) Intrinsic{kGLSL_STD_450_IntrinsicOpcodeKind, \
805*c8dee2aaSAndroid Build Coastguard Worker                                                        GLSLstd450 ## ifFloat,             \
806*c8dee2aaSAndroid Build Coastguard Worker                                                        GLSLstd450 ## ifInt,               \
807*c8dee2aaSAndroid Build Coastguard Worker                                                        GLSLstd450 ## ifUInt,              \
808*c8dee2aaSAndroid Build Coastguard Worker                                                        SpvOpUndef}
809*c8dee2aaSAndroid Build Coastguard Worker #define ALL_SPIRV(x) Intrinsic{kSPIRV_IntrinsicOpcodeKind, \
810*c8dee2aaSAndroid Build Coastguard Worker                                SpvOp ## x, SpvOp ## x, SpvOp ## x, SpvOp ## x}
811*c8dee2aaSAndroid Build Coastguard Worker #define BOOL_SPIRV(x) Intrinsic{kSPIRV_IntrinsicOpcodeKind, \
812*c8dee2aaSAndroid Build Coastguard Worker                                 SpvOpUndef, SpvOpUndef, SpvOpUndef, SpvOp ## x}
813*c8dee2aaSAndroid Build Coastguard Worker #define FLOAT_SPIRV(x) Intrinsic{kSPIRV_IntrinsicOpcodeKind, \
814*c8dee2aaSAndroid Build Coastguard Worker                                  SpvOp ## x, SpvOpUndef, SpvOpUndef, SpvOpUndef}
815*c8dee2aaSAndroid Build Coastguard Worker #define SPECIAL(x) Intrinsic{kSpecial_IntrinsicOpcodeKind, k ## x ## _SpecialIntrinsic, \
816*c8dee2aaSAndroid Build Coastguard Worker                              k ## x ## _SpecialIntrinsic, k ## x ## _SpecialIntrinsic,  \
817*c8dee2aaSAndroid Build Coastguard Worker                              k ## x ## _SpecialIntrinsic}
818*c8dee2aaSAndroid Build Coastguard Worker 
819*c8dee2aaSAndroid Build Coastguard Worker     switch (ik) {
820*c8dee2aaSAndroid Build Coastguard Worker         case k_round_IntrinsicKind:          return ALL_GLSL(Round);
821*c8dee2aaSAndroid Build Coastguard Worker         case k_roundEven_IntrinsicKind:      return ALL_GLSL(RoundEven);
822*c8dee2aaSAndroid Build Coastguard Worker         case k_trunc_IntrinsicKind:          return ALL_GLSL(Trunc);
823*c8dee2aaSAndroid Build Coastguard Worker         case k_abs_IntrinsicKind:            return BY_TYPE_GLSL(FAbs, SAbs, SAbs);
824*c8dee2aaSAndroid Build Coastguard Worker         case k_sign_IntrinsicKind:           return BY_TYPE_GLSL(FSign, SSign, SSign);
825*c8dee2aaSAndroid Build Coastguard Worker         case k_floor_IntrinsicKind:          return ALL_GLSL(Floor);
826*c8dee2aaSAndroid Build Coastguard Worker         case k_ceil_IntrinsicKind:           return ALL_GLSL(Ceil);
827*c8dee2aaSAndroid Build Coastguard Worker         case k_fract_IntrinsicKind:          return ALL_GLSL(Fract);
828*c8dee2aaSAndroid Build Coastguard Worker         case k_radians_IntrinsicKind:        return ALL_GLSL(Radians);
829*c8dee2aaSAndroid Build Coastguard Worker         case k_degrees_IntrinsicKind:        return ALL_GLSL(Degrees);
830*c8dee2aaSAndroid Build Coastguard Worker         case k_sin_IntrinsicKind:            return ALL_GLSL(Sin);
831*c8dee2aaSAndroid Build Coastguard Worker         case k_cos_IntrinsicKind:            return ALL_GLSL(Cos);
832*c8dee2aaSAndroid Build Coastguard Worker         case k_tan_IntrinsicKind:            return ALL_GLSL(Tan);
833*c8dee2aaSAndroid Build Coastguard Worker         case k_asin_IntrinsicKind:           return ALL_GLSL(Asin);
834*c8dee2aaSAndroid Build Coastguard Worker         case k_acos_IntrinsicKind:           return ALL_GLSL(Acos);
835*c8dee2aaSAndroid Build Coastguard Worker         case k_atan_IntrinsicKind:           return SPECIAL(Atan);
836*c8dee2aaSAndroid Build Coastguard Worker         case k_sinh_IntrinsicKind:           return ALL_GLSL(Sinh);
837*c8dee2aaSAndroid Build Coastguard Worker         case k_cosh_IntrinsicKind:           return ALL_GLSL(Cosh);
838*c8dee2aaSAndroid Build Coastguard Worker         case k_tanh_IntrinsicKind:           return ALL_GLSL(Tanh);
839*c8dee2aaSAndroid Build Coastguard Worker         case k_asinh_IntrinsicKind:          return ALL_GLSL(Asinh);
840*c8dee2aaSAndroid Build Coastguard Worker         case k_acosh_IntrinsicKind:          return ALL_GLSL(Acosh);
841*c8dee2aaSAndroid Build Coastguard Worker         case k_atanh_IntrinsicKind:          return ALL_GLSL(Atanh);
842*c8dee2aaSAndroid Build Coastguard Worker         case k_pow_IntrinsicKind:            return ALL_GLSL(Pow);
843*c8dee2aaSAndroid Build Coastguard Worker         case k_exp_IntrinsicKind:            return ALL_GLSL(Exp);
844*c8dee2aaSAndroid Build Coastguard Worker         case k_log_IntrinsicKind:            return ALL_GLSL(Log);
845*c8dee2aaSAndroid Build Coastguard Worker         case k_exp2_IntrinsicKind:           return ALL_GLSL(Exp2);
846*c8dee2aaSAndroid Build Coastguard Worker         case k_log2_IntrinsicKind:           return ALL_GLSL(Log2);
847*c8dee2aaSAndroid Build Coastguard Worker         case k_sqrt_IntrinsicKind:           return ALL_GLSL(Sqrt);
848*c8dee2aaSAndroid Build Coastguard Worker         case k_inverse_IntrinsicKind:        return ALL_GLSL(MatrixInverse);
849*c8dee2aaSAndroid Build Coastguard Worker         case k_outerProduct_IntrinsicKind:   return ALL_SPIRV(OuterProduct);
850*c8dee2aaSAndroid Build Coastguard Worker         case k_transpose_IntrinsicKind:      return ALL_SPIRV(Transpose);
851*c8dee2aaSAndroid Build Coastguard Worker         case k_isinf_IntrinsicKind:          return ALL_SPIRV(IsInf);
852*c8dee2aaSAndroid Build Coastguard Worker         case k_isnan_IntrinsicKind:          return ALL_SPIRV(IsNan);
853*c8dee2aaSAndroid Build Coastguard Worker         case k_inversesqrt_IntrinsicKind:    return ALL_GLSL(InverseSqrt);
854*c8dee2aaSAndroid Build Coastguard Worker         case k_determinant_IntrinsicKind:    return ALL_GLSL(Determinant);
855*c8dee2aaSAndroid Build Coastguard Worker         case k_matrixCompMult_IntrinsicKind: return SPECIAL(MatrixCompMult);
856*c8dee2aaSAndroid Build Coastguard Worker         case k_matrixInverse_IntrinsicKind:  return ALL_GLSL(MatrixInverse);
857*c8dee2aaSAndroid Build Coastguard Worker         case k_mod_IntrinsicKind:            return SPECIAL(Mod);
858*c8dee2aaSAndroid Build Coastguard Worker         case k_modf_IntrinsicKind:           return ALL_GLSL(Modf);
859*c8dee2aaSAndroid Build Coastguard Worker         case k_min_IntrinsicKind:            return SPECIAL(Min);
860*c8dee2aaSAndroid Build Coastguard Worker         case k_max_IntrinsicKind:            return SPECIAL(Max);
861*c8dee2aaSAndroid Build Coastguard Worker         case k_clamp_IntrinsicKind:          return SPECIAL(Clamp);
862*c8dee2aaSAndroid Build Coastguard Worker         case k_saturate_IntrinsicKind:       return SPECIAL(Saturate);
863*c8dee2aaSAndroid Build Coastguard Worker         case k_dot_IntrinsicKind:            return FLOAT_SPIRV(Dot);
864*c8dee2aaSAndroid Build Coastguard Worker         case k_mix_IntrinsicKind:            return SPECIAL(Mix);
865*c8dee2aaSAndroid Build Coastguard Worker         case k_step_IntrinsicKind:           return SPECIAL(Step);
866*c8dee2aaSAndroid Build Coastguard Worker         case k_smoothstep_IntrinsicKind:     return SPECIAL(SmoothStep);
867*c8dee2aaSAndroid Build Coastguard Worker         case k_fma_IntrinsicKind:            return ALL_GLSL(Fma);
868*c8dee2aaSAndroid Build Coastguard Worker         case k_frexp_IntrinsicKind:          return ALL_GLSL(Frexp);
869*c8dee2aaSAndroid Build Coastguard Worker         case k_ldexp_IntrinsicKind:          return ALL_GLSL(Ldexp);
870*c8dee2aaSAndroid Build Coastguard Worker 
871*c8dee2aaSAndroid Build Coastguard Worker #define PACK(type) case k_pack##type##_IntrinsicKind:   return ALL_GLSL(Pack##type); \
872*c8dee2aaSAndroid Build Coastguard Worker                    case k_unpack##type##_IntrinsicKind: return ALL_GLSL(Unpack##type)
873*c8dee2aaSAndroid Build Coastguard Worker         PACK(Snorm4x8);
874*c8dee2aaSAndroid Build Coastguard Worker         PACK(Unorm4x8);
875*c8dee2aaSAndroid Build Coastguard Worker         PACK(Snorm2x16);
876*c8dee2aaSAndroid Build Coastguard Worker         PACK(Unorm2x16);
877*c8dee2aaSAndroid Build Coastguard Worker         PACK(Half2x16);
878*c8dee2aaSAndroid Build Coastguard Worker #undef PACK
879*c8dee2aaSAndroid Build Coastguard Worker 
880*c8dee2aaSAndroid Build Coastguard Worker         case k_length_IntrinsicKind:        return ALL_GLSL(Length);
881*c8dee2aaSAndroid Build Coastguard Worker         case k_distance_IntrinsicKind:      return ALL_GLSL(Distance);
882*c8dee2aaSAndroid Build Coastguard Worker         case k_cross_IntrinsicKind:         return ALL_GLSL(Cross);
883*c8dee2aaSAndroid Build Coastguard Worker         case k_normalize_IntrinsicKind:     return ALL_GLSL(Normalize);
884*c8dee2aaSAndroid Build Coastguard Worker         case k_faceforward_IntrinsicKind:   return ALL_GLSL(FaceForward);
885*c8dee2aaSAndroid Build Coastguard Worker         case k_reflect_IntrinsicKind:       return ALL_GLSL(Reflect);
886*c8dee2aaSAndroid Build Coastguard Worker         case k_refract_IntrinsicKind:       return ALL_GLSL(Refract);
887*c8dee2aaSAndroid Build Coastguard Worker         case k_bitCount_IntrinsicKind:      return ALL_SPIRV(BitCount);
888*c8dee2aaSAndroid Build Coastguard Worker         case k_findLSB_IntrinsicKind:       return ALL_GLSL(FindILsb);
889*c8dee2aaSAndroid Build Coastguard Worker         case k_findMSB_IntrinsicKind:       return BY_TYPE_GLSL(FindSMsb, FindSMsb, FindUMsb);
890*c8dee2aaSAndroid Build Coastguard Worker         case k_dFdx_IntrinsicKind:          return FLOAT_SPIRV(DPdx);
891*c8dee2aaSAndroid Build Coastguard Worker         case k_dFdy_IntrinsicKind:          return SPECIAL(DFdy);
892*c8dee2aaSAndroid Build Coastguard Worker         case k_fwidth_IntrinsicKind:        return FLOAT_SPIRV(Fwidth);
893*c8dee2aaSAndroid Build Coastguard Worker 
894*c8dee2aaSAndroid Build Coastguard Worker         case k_sample_IntrinsicKind:      return SPECIAL(Texture);
895*c8dee2aaSAndroid Build Coastguard Worker         case k_sampleGrad_IntrinsicKind:  return SPECIAL(TextureGrad);
896*c8dee2aaSAndroid Build Coastguard Worker         case k_sampleLod_IntrinsicKind:   return SPECIAL(TextureLod);
897*c8dee2aaSAndroid Build Coastguard Worker         case k_subpassLoad_IntrinsicKind: return SPECIAL(SubpassLoad);
898*c8dee2aaSAndroid Build Coastguard Worker 
899*c8dee2aaSAndroid Build Coastguard Worker         case k_textureRead_IntrinsicKind:  return SPECIAL(TextureRead);
900*c8dee2aaSAndroid Build Coastguard Worker         case k_textureWrite_IntrinsicKind:  return SPECIAL(TextureWrite);
901*c8dee2aaSAndroid Build Coastguard Worker         case k_textureWidth_IntrinsicKind:  return SPECIAL(TextureWidth);
902*c8dee2aaSAndroid Build Coastguard Worker         case k_textureHeight_IntrinsicKind:  return SPECIAL(TextureHeight);
903*c8dee2aaSAndroid Build Coastguard Worker 
904*c8dee2aaSAndroid Build Coastguard Worker         case k_floatBitsToInt_IntrinsicKind:  return ALL_SPIRV(Bitcast);
905*c8dee2aaSAndroid Build Coastguard Worker         case k_floatBitsToUint_IntrinsicKind: return ALL_SPIRV(Bitcast);
906*c8dee2aaSAndroid Build Coastguard Worker         case k_intBitsToFloat_IntrinsicKind:  return ALL_SPIRV(Bitcast);
907*c8dee2aaSAndroid Build Coastguard Worker         case k_uintBitsToFloat_IntrinsicKind: return ALL_SPIRV(Bitcast);
908*c8dee2aaSAndroid Build Coastguard Worker 
909*c8dee2aaSAndroid Build Coastguard Worker         case k_any_IntrinsicKind:   return BOOL_SPIRV(Any);
910*c8dee2aaSAndroid Build Coastguard Worker         case k_all_IntrinsicKind:   return BOOL_SPIRV(All);
911*c8dee2aaSAndroid Build Coastguard Worker         case k_not_IntrinsicKind:   return BOOL_SPIRV(LogicalNot);
912*c8dee2aaSAndroid Build Coastguard Worker 
913*c8dee2aaSAndroid Build Coastguard Worker         case k_equal_IntrinsicKind:
914*c8dee2aaSAndroid Build Coastguard Worker             return Intrinsic{kSPIRV_IntrinsicOpcodeKind,
915*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpFOrdEqual,
916*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpIEqual,
917*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpIEqual,
918*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpLogicalEqual};
919*c8dee2aaSAndroid Build Coastguard Worker         case k_notEqual_IntrinsicKind:
920*c8dee2aaSAndroid Build Coastguard Worker             return Intrinsic{kSPIRV_IntrinsicOpcodeKind,
921*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpFUnordNotEqual,
922*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpINotEqual,
923*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpINotEqual,
924*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpLogicalNotEqual};
925*c8dee2aaSAndroid Build Coastguard Worker         case k_lessThan_IntrinsicKind:
926*c8dee2aaSAndroid Build Coastguard Worker             return Intrinsic{kSPIRV_IntrinsicOpcodeKind,
927*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpFOrdLessThan,
928*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpSLessThan,
929*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpULessThan,
930*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpUndef};
931*c8dee2aaSAndroid Build Coastguard Worker         case k_lessThanEqual_IntrinsicKind:
932*c8dee2aaSAndroid Build Coastguard Worker             return Intrinsic{kSPIRV_IntrinsicOpcodeKind,
933*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpFOrdLessThanEqual,
934*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpSLessThanEqual,
935*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpULessThanEqual,
936*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpUndef};
937*c8dee2aaSAndroid Build Coastguard Worker         case k_greaterThan_IntrinsicKind:
938*c8dee2aaSAndroid Build Coastguard Worker             return Intrinsic{kSPIRV_IntrinsicOpcodeKind,
939*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpFOrdGreaterThan,
940*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpSGreaterThan,
941*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpUGreaterThan,
942*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpUndef};
943*c8dee2aaSAndroid Build Coastguard Worker         case k_greaterThanEqual_IntrinsicKind:
944*c8dee2aaSAndroid Build Coastguard Worker             return Intrinsic{kSPIRV_IntrinsicOpcodeKind,
945*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpFOrdGreaterThanEqual,
946*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpSGreaterThanEqual,
947*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpUGreaterThanEqual,
948*c8dee2aaSAndroid Build Coastguard Worker                              SpvOpUndef};
949*c8dee2aaSAndroid Build Coastguard Worker 
950*c8dee2aaSAndroid Build Coastguard Worker         case k_atomicAdd_IntrinsicKind:   return SPECIAL(AtomicAdd);
951*c8dee2aaSAndroid Build Coastguard Worker         case k_atomicLoad_IntrinsicKind:  return SPECIAL(AtomicLoad);
952*c8dee2aaSAndroid Build Coastguard Worker         case k_atomicStore_IntrinsicKind: return SPECIAL(AtomicStore);
953*c8dee2aaSAndroid Build Coastguard Worker 
954*c8dee2aaSAndroid Build Coastguard Worker         case k_storageBarrier_IntrinsicKind:   return SPECIAL(StorageBarrier);
955*c8dee2aaSAndroid Build Coastguard Worker         case k_workgroupBarrier_IntrinsicKind: return SPECIAL(WorkgroupBarrier);
956*c8dee2aaSAndroid Build Coastguard Worker 
957*c8dee2aaSAndroid Build Coastguard Worker         default:
958*c8dee2aaSAndroid Build Coastguard Worker             return Intrinsic{kInvalid_IntrinsicOpcodeKind, 0, 0, 0, 0};
959*c8dee2aaSAndroid Build Coastguard Worker     }
960*c8dee2aaSAndroid Build Coastguard Worker }
961*c8dee2aaSAndroid Build Coastguard Worker 
writeWord(int32_t word,OutputStream & out)962*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeWord(int32_t word, OutputStream& out) {
963*c8dee2aaSAndroid Build Coastguard Worker     out.write((const char*) &word, sizeof(word));
964*c8dee2aaSAndroid Build Coastguard Worker }
965*c8dee2aaSAndroid Build Coastguard Worker 
is_float(const Type & type)966*c8dee2aaSAndroid Build Coastguard Worker static bool is_float(const Type& type) {
967*c8dee2aaSAndroid Build Coastguard Worker     return (type.isScalar() || type.isVector() || type.isMatrix()) &&
968*c8dee2aaSAndroid Build Coastguard Worker            type.componentType().isFloat();
969*c8dee2aaSAndroid Build Coastguard Worker }
970*c8dee2aaSAndroid Build Coastguard Worker 
is_signed(const Type & type)971*c8dee2aaSAndroid Build Coastguard Worker static bool is_signed(const Type& type) {
972*c8dee2aaSAndroid Build Coastguard Worker     return (type.isScalar() || type.isVector()) && type.componentType().isSigned();
973*c8dee2aaSAndroid Build Coastguard Worker }
974*c8dee2aaSAndroid Build Coastguard Worker 
is_unsigned(const Type & type)975*c8dee2aaSAndroid Build Coastguard Worker static bool is_unsigned(const Type& type) {
976*c8dee2aaSAndroid Build Coastguard Worker     return (type.isScalar() || type.isVector()) && type.componentType().isUnsigned();
977*c8dee2aaSAndroid Build Coastguard Worker }
978*c8dee2aaSAndroid Build Coastguard Worker 
is_bool(const Type & type)979*c8dee2aaSAndroid Build Coastguard Worker static bool is_bool(const Type& type) {
980*c8dee2aaSAndroid Build Coastguard Worker     return (type.isScalar() || type.isVector()) && type.componentType().isBoolean();
981*c8dee2aaSAndroid Build Coastguard Worker }
982*c8dee2aaSAndroid Build Coastguard Worker 
983*c8dee2aaSAndroid Build Coastguard Worker template <typename T>
pick_by_type(const Type & type,T ifFloat,T ifInt,T ifUInt,T ifBool)984*c8dee2aaSAndroid Build Coastguard Worker static T pick_by_type(const Type& type, T ifFloat, T ifInt, T ifUInt, T ifBool) {
985*c8dee2aaSAndroid Build Coastguard Worker     if (is_float(type)) {
986*c8dee2aaSAndroid Build Coastguard Worker         return ifFloat;
987*c8dee2aaSAndroid Build Coastguard Worker     }
988*c8dee2aaSAndroid Build Coastguard Worker     if (is_signed(type)) {
989*c8dee2aaSAndroid Build Coastguard Worker         return ifInt;
990*c8dee2aaSAndroid Build Coastguard Worker     }
991*c8dee2aaSAndroid Build Coastguard Worker     if (is_unsigned(type)) {
992*c8dee2aaSAndroid Build Coastguard Worker         return ifUInt;
993*c8dee2aaSAndroid Build Coastguard Worker     }
994*c8dee2aaSAndroid Build Coastguard Worker     if (is_bool(type)) {
995*c8dee2aaSAndroid Build Coastguard Worker         return ifBool;
996*c8dee2aaSAndroid Build Coastguard Worker     }
997*c8dee2aaSAndroid Build Coastguard Worker     SkDEBUGFAIL("unrecognized type");
998*c8dee2aaSAndroid Build Coastguard Worker     return ifFloat;
999*c8dee2aaSAndroid Build Coastguard Worker }
1000*c8dee2aaSAndroid Build Coastguard Worker 
is_out(ModifierFlags f)1001*c8dee2aaSAndroid Build Coastguard Worker static bool is_out(ModifierFlags f) {
1002*c8dee2aaSAndroid Build Coastguard Worker     return SkToBool(f & ModifierFlag::kOut);
1003*c8dee2aaSAndroid Build Coastguard Worker }
1004*c8dee2aaSAndroid Build Coastguard Worker 
is_in(ModifierFlags f)1005*c8dee2aaSAndroid Build Coastguard Worker static bool is_in(ModifierFlags f) {
1006*c8dee2aaSAndroid Build Coastguard Worker     if (f & ModifierFlag::kIn) {
1007*c8dee2aaSAndroid Build Coastguard Worker         return true;  // `in` and `inout` both count
1008*c8dee2aaSAndroid Build Coastguard Worker     }
1009*c8dee2aaSAndroid Build Coastguard Worker     // If neither in/out flag is set, the type is implicitly `in`.
1010*c8dee2aaSAndroid Build Coastguard Worker     return !SkToBool(f & ModifierFlag::kOut);
1011*c8dee2aaSAndroid Build Coastguard Worker }
1012*c8dee2aaSAndroid Build Coastguard Worker 
is_control_flow_op(SpvOp_ op)1013*c8dee2aaSAndroid Build Coastguard Worker static bool is_control_flow_op(SpvOp_ op) {
1014*c8dee2aaSAndroid Build Coastguard Worker     switch (op) {
1015*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpReturn:
1016*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpReturnValue:
1017*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpKill:
1018*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpSwitch:
1019*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpBranch:
1020*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpBranchConditional:
1021*c8dee2aaSAndroid Build Coastguard Worker             return true;
1022*c8dee2aaSAndroid Build Coastguard Worker         default:
1023*c8dee2aaSAndroid Build Coastguard Worker             return false;
1024*c8dee2aaSAndroid Build Coastguard Worker     }
1025*c8dee2aaSAndroid Build Coastguard Worker }
1026*c8dee2aaSAndroid Build Coastguard Worker 
is_globally_reachable_op(SpvOp_ op)1027*c8dee2aaSAndroid Build Coastguard Worker static bool is_globally_reachable_op(SpvOp_ op) {
1028*c8dee2aaSAndroid Build Coastguard Worker     switch (op) {
1029*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstant:
1030*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstantTrue:
1031*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstantFalse:
1032*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstantComposite:
1033*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeVoid:
1034*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeInt:
1035*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeFloat:
1036*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeBool:
1037*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeVector:
1038*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeMatrix:
1039*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeArray:
1040*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypePointer:
1041*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeFunction:
1042*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeRuntimeArray:
1043*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeStruct:
1044*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeImage:
1045*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeSampledImage:
1046*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpTypeSampler:
1047*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpVariable:
1048*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpFunction:
1049*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpFunctionParameter:
1050*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpFunctionEnd:
1051*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpExecutionMode:
1052*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpMemoryModel:
1053*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpCapability:
1054*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpExtInstImport:
1055*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpEntryPoint:
1056*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpSource:
1057*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpSourceExtension:
1058*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpName:
1059*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpMemberName:
1060*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpDecorate:
1061*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpMemberDecorate:
1062*c8dee2aaSAndroid Build Coastguard Worker             return true;
1063*c8dee2aaSAndroid Build Coastguard Worker         default:
1064*c8dee2aaSAndroid Build Coastguard Worker             return false;
1065*c8dee2aaSAndroid Build Coastguard Worker     }
1066*c8dee2aaSAndroid Build Coastguard Worker }
1067*c8dee2aaSAndroid Build Coastguard Worker 
writeOpCode(SpvOp_ opCode,int length,OutputStream & out)1068*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeOpCode(SpvOp_ opCode, int length, OutputStream& out) {
1069*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(opCode != SpvOpLoad || &out != &fConstantBuffer);
1070*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(opCode != SpvOpUndef);
1071*c8dee2aaSAndroid Build Coastguard Worker     bool foundDeadCode = false;
1072*c8dee2aaSAndroid Build Coastguard Worker     if (is_control_flow_op(opCode)) {
1073*c8dee2aaSAndroid Build Coastguard Worker         // This instruction causes us to leave the current block.
1074*c8dee2aaSAndroid Build Coastguard Worker         foundDeadCode = (fCurrentBlock == 0);
1075*c8dee2aaSAndroid Build Coastguard Worker         fCurrentBlock = 0;
1076*c8dee2aaSAndroid Build Coastguard Worker     } else if (!is_globally_reachable_op(opCode)) {
1077*c8dee2aaSAndroid Build Coastguard Worker         foundDeadCode = (fCurrentBlock == 0);
1078*c8dee2aaSAndroid Build Coastguard Worker     }
1079*c8dee2aaSAndroid Build Coastguard Worker 
1080*c8dee2aaSAndroid Build Coastguard Worker     if (foundDeadCode) {
1081*c8dee2aaSAndroid Build Coastguard Worker         // We just encountered dead code--an instruction that don't have an associated block.
1082*c8dee2aaSAndroid Build Coastguard Worker         // Synthesize a label if this happens; this is necessary to satisfy the validator.
1083*c8dee2aaSAndroid Build Coastguard Worker         this->writeLabel(this->nextId(nullptr), kBranchlessBlock, out);
1084*c8dee2aaSAndroid Build Coastguard Worker     }
1085*c8dee2aaSAndroid Build Coastguard Worker 
1086*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord((length << 16) | opCode, out);
1087*c8dee2aaSAndroid Build Coastguard Worker }
1088*c8dee2aaSAndroid Build Coastguard Worker 
writeLabel(SpvId label,StraightLineLabelType,OutputStream & out)1089*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeLabel(SpvId label, StraightLineLabelType, OutputStream& out) {
1090*c8dee2aaSAndroid Build Coastguard Worker     // The straight-line label type is not important; in any case, no caches are invalidated.
1091*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fCurrentBlock);
1092*c8dee2aaSAndroid Build Coastguard Worker     fCurrentBlock = label;
1093*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpLabel, label, out);
1094*c8dee2aaSAndroid Build Coastguard Worker }
1095*c8dee2aaSAndroid Build Coastguard Worker 
writeLabel(SpvId label,BranchingLabelType type,ConditionalOpCounts ops,OutputStream & out)1096*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeLabel(SpvId label, BranchingLabelType type,
1097*c8dee2aaSAndroid Build Coastguard Worker                                     ConditionalOpCounts ops, OutputStream& out) {
1098*c8dee2aaSAndroid Build Coastguard Worker     switch (type) {
1099*c8dee2aaSAndroid Build Coastguard Worker         case kBranchIsBelow:
1100*c8dee2aaSAndroid Build Coastguard Worker         case kBranchesOnBothSides:
1101*c8dee2aaSAndroid Build Coastguard Worker             // With a backward or bidirectional branch, we haven't seen the code between the label
1102*c8dee2aaSAndroid Build Coastguard Worker             // and the branch yet, so any stored value is potentially suspect. Without scanning
1103*c8dee2aaSAndroid Build Coastguard Worker             // ahead to check, the only safe option is to ditch the store cache entirely.
1104*c8dee2aaSAndroid Build Coastguard Worker             fStoreCache.reset();
1105*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
1106*c8dee2aaSAndroid Build Coastguard Worker 
1107*c8dee2aaSAndroid Build Coastguard Worker         case kBranchIsAbove:
1108*c8dee2aaSAndroid Build Coastguard Worker             // With a forward branch, we can rely on stores that we had cached at the start of the
1109*c8dee2aaSAndroid Build Coastguard Worker             // statement/expression, if they haven't been touched yet. Anything newer than that is
1110*c8dee2aaSAndroid Build Coastguard Worker             // pruned.
1111*c8dee2aaSAndroid Build Coastguard Worker             this->pruneConditionalOps(ops);
1112*c8dee2aaSAndroid Build Coastguard Worker             break;
1113*c8dee2aaSAndroid Build Coastguard Worker     }
1114*c8dee2aaSAndroid Build Coastguard Worker 
1115*c8dee2aaSAndroid Build Coastguard Worker     // Emit the label.
1116*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(label, kBranchlessBlock, out);
1117*c8dee2aaSAndroid Build Coastguard Worker }
1118*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,OutputStream & out)1119*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, OutputStream& out) {
1120*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 1, out);
1121*c8dee2aaSAndroid Build Coastguard Worker }
1122*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,int32_t word1,OutputStream & out)1123*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, OutputStream& out) {
1124*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 2, out);
1125*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word1, out);
1126*c8dee2aaSAndroid Build Coastguard Worker }
1127*c8dee2aaSAndroid Build Coastguard Worker 
writeString(std::string_view s,OutputStream & out)1128*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeString(std::string_view s, OutputStream& out) {
1129*c8dee2aaSAndroid Build Coastguard Worker     out.write(s.data(), s.length());
1130*c8dee2aaSAndroid Build Coastguard Worker     switch (s.length() % 4) {
1131*c8dee2aaSAndroid Build Coastguard Worker         case 1:
1132*c8dee2aaSAndroid Build Coastguard Worker             out.write8(0);
1133*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
1134*c8dee2aaSAndroid Build Coastguard Worker         case 2:
1135*c8dee2aaSAndroid Build Coastguard Worker             out.write8(0);
1136*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
1137*c8dee2aaSAndroid Build Coastguard Worker         case 3:
1138*c8dee2aaSAndroid Build Coastguard Worker             out.write8(0);
1139*c8dee2aaSAndroid Build Coastguard Worker             break;
1140*c8dee2aaSAndroid Build Coastguard Worker         default:
1141*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(0, out);
1142*c8dee2aaSAndroid Build Coastguard Worker             break;
1143*c8dee2aaSAndroid Build Coastguard Worker     }
1144*c8dee2aaSAndroid Build Coastguard Worker }
1145*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,std::string_view string,OutputStream & out)1146*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, std::string_view string,
1147*c8dee2aaSAndroid Build Coastguard Worker                                           OutputStream& out) {
1148*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 1 + (string.length() + 4) / 4, out);
1149*c8dee2aaSAndroid Build Coastguard Worker     this->writeString(string, out);
1150*c8dee2aaSAndroid Build Coastguard Worker }
1151*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,int32_t word1,std::string_view string,OutputStream & out)1152*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, std::string_view string,
1153*c8dee2aaSAndroid Build Coastguard Worker                                           OutputStream& out) {
1154*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 2 + (string.length() + 4) / 4, out);
1155*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word1, out);
1156*c8dee2aaSAndroid Build Coastguard Worker     this->writeString(string, out);
1157*c8dee2aaSAndroid Build Coastguard Worker }
1158*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,int32_t word1,int32_t word2,std::string_view string,OutputStream & out)1159*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
1160*c8dee2aaSAndroid Build Coastguard Worker                                           std::string_view string, OutputStream& out) {
1161*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 3 + (string.length() + 4) / 4, out);
1162*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word1, out);
1163*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word2, out);
1164*c8dee2aaSAndroid Build Coastguard Worker     this->writeString(string, out);
1165*c8dee2aaSAndroid Build Coastguard Worker }
1166*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,int32_t word1,int32_t word2,OutputStream & out)1167*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
1168*c8dee2aaSAndroid Build Coastguard Worker                                           OutputStream& out) {
1169*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 3, out);
1170*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word1, out);
1171*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word2, out);
1172*c8dee2aaSAndroid Build Coastguard Worker }
1173*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,int32_t word1,int32_t word2,int32_t word3,OutputStream & out)1174*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
1175*c8dee2aaSAndroid Build Coastguard Worker                                           int32_t word3, OutputStream& out) {
1176*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 4, out);
1177*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word1, out);
1178*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word2, out);
1179*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word3, out);
1180*c8dee2aaSAndroid Build Coastguard Worker }
1181*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,int32_t word1,int32_t word2,int32_t word3,int32_t word4,OutputStream & out)1182*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
1183*c8dee2aaSAndroid Build Coastguard Worker                                           int32_t word3, int32_t word4, OutputStream& out) {
1184*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 5, out);
1185*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word1, out);
1186*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word2, out);
1187*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word3, out);
1188*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word4, out);
1189*c8dee2aaSAndroid Build Coastguard Worker }
1190*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,int32_t word1,int32_t word2,int32_t word3,int32_t word4,int32_t word5,OutputStream & out)1191*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
1192*c8dee2aaSAndroid Build Coastguard Worker                                           int32_t word3, int32_t word4, int32_t word5,
1193*c8dee2aaSAndroid Build Coastguard Worker                                           OutputStream& out) {
1194*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 6, out);
1195*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word1, out);
1196*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word2, out);
1197*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word3, out);
1198*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word4, out);
1199*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word5, out);
1200*c8dee2aaSAndroid Build Coastguard Worker }
1201*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,int32_t word1,int32_t word2,int32_t word3,int32_t word4,int32_t word5,int32_t word6,OutputStream & out)1202*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
1203*c8dee2aaSAndroid Build Coastguard Worker                                           int32_t word3, int32_t word4, int32_t word5,
1204*c8dee2aaSAndroid Build Coastguard Worker                                           int32_t word6, OutputStream& out) {
1205*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 7, out);
1206*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word1, out);
1207*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word2, out);
1208*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word3, out);
1209*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word4, out);
1210*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word5, out);
1211*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word6, out);
1212*c8dee2aaSAndroid Build Coastguard Worker }
1213*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,int32_t word1,int32_t word2,int32_t word3,int32_t word4,int32_t word5,int32_t word6,int32_t word7,OutputStream & out)1214*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
1215*c8dee2aaSAndroid Build Coastguard Worker                                           int32_t word3, int32_t word4, int32_t word5,
1216*c8dee2aaSAndroid Build Coastguard Worker                                           int32_t word6, int32_t word7, OutputStream& out) {
1217*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 8, out);
1218*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word1, out);
1219*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word2, out);
1220*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word3, out);
1221*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word4, out);
1222*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word5, out);
1223*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word6, out);
1224*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word7, out);
1225*c8dee2aaSAndroid Build Coastguard Worker }
1226*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,int32_t word1,int32_t word2,int32_t word3,int32_t word4,int32_t word5,int32_t word6,int32_t word7,int32_t word8,OutputStream & out)1227*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode, int32_t word1, int32_t word2,
1228*c8dee2aaSAndroid Build Coastguard Worker                                           int32_t word3, int32_t word4, int32_t word5,
1229*c8dee2aaSAndroid Build Coastguard Worker                                           int32_t word6, int32_t word7, int32_t word8,
1230*c8dee2aaSAndroid Build Coastguard Worker                                           OutputStream& out) {
1231*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, 9, out);
1232*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word1, out);
1233*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word2, out);
1234*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word3, out);
1235*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word4, out);
1236*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word5, out);
1237*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word6, out);
1238*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word7, out);
1239*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(word8, out);
1240*c8dee2aaSAndroid Build Coastguard Worker }
1241*c8dee2aaSAndroid Build Coastguard Worker 
BuildInstructionKey(SpvOp_ opCode,const TArray<Word> & words)1242*c8dee2aaSAndroid Build Coastguard Worker SPIRVCodeGenerator::Instruction SPIRVCodeGenerator::BuildInstructionKey(SpvOp_ opCode,
1243*c8dee2aaSAndroid Build Coastguard Worker                                                                         const TArray<Word>& words) {
1244*c8dee2aaSAndroid Build Coastguard Worker     // Assemble a cache key for this instruction.
1245*c8dee2aaSAndroid Build Coastguard Worker     Instruction key;
1246*c8dee2aaSAndroid Build Coastguard Worker     key.fOp = opCode;
1247*c8dee2aaSAndroid Build Coastguard Worker     key.fWords.resize(words.size());
1248*c8dee2aaSAndroid Build Coastguard Worker     key.fResultKind = Word::Kind::kNone;
1249*c8dee2aaSAndroid Build Coastguard Worker 
1250*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < words.size(); ++index) {
1251*c8dee2aaSAndroid Build Coastguard Worker         const Word& word = words[index];
1252*c8dee2aaSAndroid Build Coastguard Worker         key.fWords[index] = word.fValue;
1253*c8dee2aaSAndroid Build Coastguard Worker         if (word.isResult()) {
1254*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(key.fResultKind == Word::Kind::kNone);
1255*c8dee2aaSAndroid Build Coastguard Worker             key.fResultKind = word.fKind;
1256*c8dee2aaSAndroid Build Coastguard Worker         }
1257*c8dee2aaSAndroid Build Coastguard Worker     }
1258*c8dee2aaSAndroid Build Coastguard Worker 
1259*c8dee2aaSAndroid Build Coastguard Worker     return key;
1260*c8dee2aaSAndroid Build Coastguard Worker }
1261*c8dee2aaSAndroid Build Coastguard Worker 
writeInstruction(SpvOp_ opCode,const TArray<Word> & words,OutputStream & out)1262*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeInstruction(SpvOp_ opCode,
1263*c8dee2aaSAndroid Build Coastguard Worker                                            const TArray<Word>& words,
1264*c8dee2aaSAndroid Build Coastguard Worker                                            OutputStream& out) {
1265*c8dee2aaSAndroid Build Coastguard Worker     // writeOpLoad and writeOpStore have dedicated code.
1266*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(opCode != SpvOpLoad);
1267*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(opCode != SpvOpStore);
1268*c8dee2aaSAndroid Build Coastguard Worker 
1269*c8dee2aaSAndroid Build Coastguard Worker     // If this instruction exists in our op cache, return the cached SpvId.
1270*c8dee2aaSAndroid Build Coastguard Worker     Instruction key = BuildInstructionKey(opCode, words);
1271*c8dee2aaSAndroid Build Coastguard Worker     if (SpvId* cachedOp = fOpCache.find(key)) {
1272*c8dee2aaSAndroid Build Coastguard Worker         return *cachedOp;
1273*c8dee2aaSAndroid Build Coastguard Worker     }
1274*c8dee2aaSAndroid Build Coastguard Worker 
1275*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = NA;
1276*c8dee2aaSAndroid Build Coastguard Worker     Precision precision = Precision::kDefault;
1277*c8dee2aaSAndroid Build Coastguard Worker 
1278*c8dee2aaSAndroid Build Coastguard Worker     switch (key.fResultKind) {
1279*c8dee2aaSAndroid Build Coastguard Worker         case Word::Kind::kUniqueResult:
1280*c8dee2aaSAndroid Build Coastguard Worker             // The instruction returns a SpvId, but we do not want deduplication.
1281*c8dee2aaSAndroid Build Coastguard Worker             result = this->nextId(Precision::kDefault);
1282*c8dee2aaSAndroid Build Coastguard Worker             fSpvIdCache.set(result, key);
1283*c8dee2aaSAndroid Build Coastguard Worker             break;
1284*c8dee2aaSAndroid Build Coastguard Worker 
1285*c8dee2aaSAndroid Build Coastguard Worker         case Word::Kind::kNone:
1286*c8dee2aaSAndroid Build Coastguard Worker             // The instruction doesn't return a SpvId, but we can still cache and deduplicate it.
1287*c8dee2aaSAndroid Build Coastguard Worker             fOpCache.set(key, result);
1288*c8dee2aaSAndroid Build Coastguard Worker             break;
1289*c8dee2aaSAndroid Build Coastguard Worker 
1290*c8dee2aaSAndroid Build Coastguard Worker         case Word::Kind::kRelaxedPrecisionResult:
1291*c8dee2aaSAndroid Build Coastguard Worker             precision = Precision::kRelaxed;
1292*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
1293*c8dee2aaSAndroid Build Coastguard Worker 
1294*c8dee2aaSAndroid Build Coastguard Worker         case Word::Kind::kKeyedResult:
1295*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
1296*c8dee2aaSAndroid Build Coastguard Worker 
1297*c8dee2aaSAndroid Build Coastguard Worker         case Word::Kind::kDefaultPrecisionResult:
1298*c8dee2aaSAndroid Build Coastguard Worker             // Consume a new SpvId.
1299*c8dee2aaSAndroid Build Coastguard Worker             result = this->nextId(precision);
1300*c8dee2aaSAndroid Build Coastguard Worker             fOpCache.set(key, result);
1301*c8dee2aaSAndroid Build Coastguard Worker             fSpvIdCache.set(result, key);
1302*c8dee2aaSAndroid Build Coastguard Worker 
1303*c8dee2aaSAndroid Build Coastguard Worker             // Globally-reachable ops are not subject to the whims of flow control.
1304*c8dee2aaSAndroid Build Coastguard Worker             if (!is_globally_reachable_op(opCode)) {
1305*c8dee2aaSAndroid Build Coastguard Worker                 fReachableOps.push_back(result);
1306*c8dee2aaSAndroid Build Coastguard Worker             }
1307*c8dee2aaSAndroid Build Coastguard Worker             break;
1308*c8dee2aaSAndroid Build Coastguard Worker 
1309*c8dee2aaSAndroid Build Coastguard Worker         default:
1310*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("unexpected result kind");
1311*c8dee2aaSAndroid Build Coastguard Worker             break;
1312*c8dee2aaSAndroid Build Coastguard Worker     }
1313*c8dee2aaSAndroid Build Coastguard Worker 
1314*c8dee2aaSAndroid Build Coastguard Worker     // Write the requested instruction.
1315*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(opCode, words.size() + 1, out);
1316*c8dee2aaSAndroid Build Coastguard Worker     for (const Word& word : words) {
1317*c8dee2aaSAndroid Build Coastguard Worker         if (word.isResult()) {
1318*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(result != NA);
1319*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(result, out);
1320*c8dee2aaSAndroid Build Coastguard Worker         } else {
1321*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(word.fValue, out);
1322*c8dee2aaSAndroid Build Coastguard Worker         }
1323*c8dee2aaSAndroid Build Coastguard Worker     }
1324*c8dee2aaSAndroid Build Coastguard Worker 
1325*c8dee2aaSAndroid Build Coastguard Worker     // Return the result.
1326*c8dee2aaSAndroid Build Coastguard Worker     return result;
1327*c8dee2aaSAndroid Build Coastguard Worker }
1328*c8dee2aaSAndroid Build Coastguard Worker 
writeOpLoad(SpvId type,Precision precision,SpvId pointer,OutputStream & out)1329*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeOpLoad(SpvId type,
1330*c8dee2aaSAndroid Build Coastguard Worker                                       Precision precision,
1331*c8dee2aaSAndroid Build Coastguard Worker                                       SpvId pointer,
1332*c8dee2aaSAndroid Build Coastguard Worker                                       OutputStream& out) {
1333*c8dee2aaSAndroid Build Coastguard Worker     // Look for this pointer in our load-cache.
1334*c8dee2aaSAndroid Build Coastguard Worker     if (SpvId* cachedOp = fStoreCache.find(pointer)) {
1335*c8dee2aaSAndroid Build Coastguard Worker         return *cachedOp;
1336*c8dee2aaSAndroid Build Coastguard Worker     }
1337*c8dee2aaSAndroid Build Coastguard Worker 
1338*c8dee2aaSAndroid Build Coastguard Worker     // Write the requested OpLoad instruction.
1339*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(precision);
1340*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpLoad, type, result, pointer, out);
1341*c8dee2aaSAndroid Build Coastguard Worker     return result;
1342*c8dee2aaSAndroid Build Coastguard Worker }
1343*c8dee2aaSAndroid Build Coastguard Worker 
writeOpStore(StorageClass storageClass,SpvId pointer,SpvId value,OutputStream & out)1344*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeOpStore(StorageClass storageClass,
1345*c8dee2aaSAndroid Build Coastguard Worker                                       SpvId pointer,
1346*c8dee2aaSAndroid Build Coastguard Worker                                       SpvId value,
1347*c8dee2aaSAndroid Build Coastguard Worker                                       OutputStream& out) {
1348*c8dee2aaSAndroid Build Coastguard Worker     // Write the uncached SpvOpStore directly.
1349*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpStore, pointer, value, out);
1350*c8dee2aaSAndroid Build Coastguard Worker 
1351*c8dee2aaSAndroid Build Coastguard Worker     if (storageClass == StorageClass::kFunction) {
1352*c8dee2aaSAndroid Build Coastguard Worker         // Insert a pointer-to-SpvId mapping into the load cache. A writeOpLoad to this pointer will
1353*c8dee2aaSAndroid Build Coastguard Worker         // return the cached value as-is.
1354*c8dee2aaSAndroid Build Coastguard Worker         fStoreCache.set(pointer, value);
1355*c8dee2aaSAndroid Build Coastguard Worker         fStoreOps.push_back(pointer);
1356*c8dee2aaSAndroid Build Coastguard Worker     }
1357*c8dee2aaSAndroid Build Coastguard Worker }
1358*c8dee2aaSAndroid Build Coastguard Worker 
writeOpConstantTrue(const Type & type)1359*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeOpConstantTrue(const Type& type) {
1360*c8dee2aaSAndroid Build Coastguard Worker     return this->writeInstruction(SpvOpConstantTrue,
1361*c8dee2aaSAndroid Build Coastguard Worker                                   Words{this->getType(type), Word::Result()},
1362*c8dee2aaSAndroid Build Coastguard Worker                                   fConstantBuffer);
1363*c8dee2aaSAndroid Build Coastguard Worker }
1364*c8dee2aaSAndroid Build Coastguard Worker 
writeOpConstantFalse(const Type & type)1365*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeOpConstantFalse(const Type& type) {
1366*c8dee2aaSAndroid Build Coastguard Worker     return this->writeInstruction(SpvOpConstantFalse,
1367*c8dee2aaSAndroid Build Coastguard Worker                                   Words{this->getType(type), Word::Result()},
1368*c8dee2aaSAndroid Build Coastguard Worker                                   fConstantBuffer);
1369*c8dee2aaSAndroid Build Coastguard Worker }
1370*c8dee2aaSAndroid Build Coastguard Worker 
writeOpConstant(const Type & type,int32_t valueBits)1371*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeOpConstant(const Type& type, int32_t valueBits) {
1372*c8dee2aaSAndroid Build Coastguard Worker     return this->writeInstruction(
1373*c8dee2aaSAndroid Build Coastguard Worker             SpvOpConstant,
1374*c8dee2aaSAndroid Build Coastguard Worker             Words{this->getType(type), Word::Result(), Word::Number(valueBits)},
1375*c8dee2aaSAndroid Build Coastguard Worker             fConstantBuffer);
1376*c8dee2aaSAndroid Build Coastguard Worker }
1377*c8dee2aaSAndroid Build Coastguard Worker 
writeOpConstantComposite(const Type & type,const TArray<SpvId> & values)1378*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeOpConstantComposite(const Type& type,
1379*c8dee2aaSAndroid Build Coastguard Worker                                                    const TArray<SpvId>& values) {
1380*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(values.size() == (type.isStruct() ? SkToInt(type.fields().size()) : type.columns()));
1381*c8dee2aaSAndroid Build Coastguard Worker 
1382*c8dee2aaSAndroid Build Coastguard Worker     Words words;
1383*c8dee2aaSAndroid Build Coastguard Worker     words.push_back(this->getType(type));
1384*c8dee2aaSAndroid Build Coastguard Worker     words.push_back(Word::Result());
1385*c8dee2aaSAndroid Build Coastguard Worker     for (SpvId value : values) {
1386*c8dee2aaSAndroid Build Coastguard Worker         words.push_back(value);
1387*c8dee2aaSAndroid Build Coastguard Worker     }
1388*c8dee2aaSAndroid Build Coastguard Worker     return this->writeInstruction(SpvOpConstantComposite, words, fConstantBuffer);
1389*c8dee2aaSAndroid Build Coastguard Worker }
1390*c8dee2aaSAndroid Build Coastguard Worker 
toConstants(SpvId value,TArray<SpvId> * constants)1391*c8dee2aaSAndroid Build Coastguard Worker bool SPIRVCodeGenerator::toConstants(SpvId value, TArray<SpvId>* constants) {
1392*c8dee2aaSAndroid Build Coastguard Worker     Instruction* instr = fSpvIdCache.find(value);
1393*c8dee2aaSAndroid Build Coastguard Worker     if (!instr) {
1394*c8dee2aaSAndroid Build Coastguard Worker         return false;
1395*c8dee2aaSAndroid Build Coastguard Worker     }
1396*c8dee2aaSAndroid Build Coastguard Worker     switch (instr->fOp) {
1397*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstant:
1398*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstantTrue:
1399*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstantFalse:
1400*c8dee2aaSAndroid Build Coastguard Worker             constants->push_back(value);
1401*c8dee2aaSAndroid Build Coastguard Worker             return true;
1402*c8dee2aaSAndroid Build Coastguard Worker 
1403*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstantComposite: // OpConstantComposite ResultType ResultID Constituents...
1404*c8dee2aaSAndroid Build Coastguard Worker             // Start at word 2 to skip past ResultType and ResultID.
1405*c8dee2aaSAndroid Build Coastguard Worker             for (int i = 2; i < instr->fWords.size(); ++i) {
1406*c8dee2aaSAndroid Build Coastguard Worker                 if (!this->toConstants(instr->fWords[i], constants)) {
1407*c8dee2aaSAndroid Build Coastguard Worker                     return false;
1408*c8dee2aaSAndroid Build Coastguard Worker                 }
1409*c8dee2aaSAndroid Build Coastguard Worker             }
1410*c8dee2aaSAndroid Build Coastguard Worker             return true;
1411*c8dee2aaSAndroid Build Coastguard Worker 
1412*c8dee2aaSAndroid Build Coastguard Worker         default:
1413*c8dee2aaSAndroid Build Coastguard Worker             return false;
1414*c8dee2aaSAndroid Build Coastguard Worker     }
1415*c8dee2aaSAndroid Build Coastguard Worker }
1416*c8dee2aaSAndroid Build Coastguard Worker 
toConstants(SkSpan<const SpvId> values,TArray<SpvId> * constants)1417*c8dee2aaSAndroid Build Coastguard Worker bool SPIRVCodeGenerator::toConstants(SkSpan<const SpvId> values, TArray<SpvId>* constants) {
1418*c8dee2aaSAndroid Build Coastguard Worker     for (SpvId value : values) {
1419*c8dee2aaSAndroid Build Coastguard Worker         if (!this->toConstants(value, constants)) {
1420*c8dee2aaSAndroid Build Coastguard Worker             return false;
1421*c8dee2aaSAndroid Build Coastguard Worker         }
1422*c8dee2aaSAndroid Build Coastguard Worker     }
1423*c8dee2aaSAndroid Build Coastguard Worker     return true;
1424*c8dee2aaSAndroid Build Coastguard Worker }
1425*c8dee2aaSAndroid Build Coastguard Worker 
writeOpCompositeConstruct(const Type & type,const TArray<SpvId> & values,OutputStream & out)1426*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeOpCompositeConstruct(const Type& type,
1427*c8dee2aaSAndroid Build Coastguard Worker                                                     const TArray<SpvId>& values,
1428*c8dee2aaSAndroid Build Coastguard Worker                                                     OutputStream& out) {
1429*c8dee2aaSAndroid Build Coastguard Worker     // If this is a vector composed entirely of literals, write a constant-composite instead.
1430*c8dee2aaSAndroid Build Coastguard Worker     if (type.isVector()) {
1431*c8dee2aaSAndroid Build Coastguard Worker         STArray<4, SpvId> constants;
1432*c8dee2aaSAndroid Build Coastguard Worker         if (this->toConstants(SkSpan(values), &constants)) {
1433*c8dee2aaSAndroid Build Coastguard Worker             // Create a vector from literals.
1434*c8dee2aaSAndroid Build Coastguard Worker             return this->writeOpConstantComposite(type, constants);
1435*c8dee2aaSAndroid Build Coastguard Worker         }
1436*c8dee2aaSAndroid Build Coastguard Worker     }
1437*c8dee2aaSAndroid Build Coastguard Worker 
1438*c8dee2aaSAndroid Build Coastguard Worker     // If this is a matrix composed entirely of literals, constant-composite them instead.
1439*c8dee2aaSAndroid Build Coastguard Worker     if (type.isMatrix()) {
1440*c8dee2aaSAndroid Build Coastguard Worker         STArray<16, SpvId> constants;
1441*c8dee2aaSAndroid Build Coastguard Worker         if (this->toConstants(SkSpan(values), &constants)) {
1442*c8dee2aaSAndroid Build Coastguard Worker             // Create each matrix column.
1443*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(type.isMatrix());
1444*c8dee2aaSAndroid Build Coastguard Worker             const Type& vecType = type.columnType(fContext);
1445*c8dee2aaSAndroid Build Coastguard Worker             STArray<4, SpvId> columnIDs;
1446*c8dee2aaSAndroid Build Coastguard Worker             for (int index=0; index < type.columns(); ++index) {
1447*c8dee2aaSAndroid Build Coastguard Worker                 STArray<4, SpvId> columnConstants(&constants[index * type.rows()],
1448*c8dee2aaSAndroid Build Coastguard Worker                                                     type.rows());
1449*c8dee2aaSAndroid Build Coastguard Worker                 columnIDs.push_back(this->writeOpConstantComposite(vecType, columnConstants));
1450*c8dee2aaSAndroid Build Coastguard Worker             }
1451*c8dee2aaSAndroid Build Coastguard Worker             // Compose the matrix from its columns.
1452*c8dee2aaSAndroid Build Coastguard Worker             return this->writeOpConstantComposite(type, columnIDs);
1453*c8dee2aaSAndroid Build Coastguard Worker         }
1454*c8dee2aaSAndroid Build Coastguard Worker     }
1455*c8dee2aaSAndroid Build Coastguard Worker 
1456*c8dee2aaSAndroid Build Coastguard Worker     Words words;
1457*c8dee2aaSAndroid Build Coastguard Worker     words.push_back(this->getType(type));
1458*c8dee2aaSAndroid Build Coastguard Worker     words.push_back(Word::Result(type));
1459*c8dee2aaSAndroid Build Coastguard Worker     for (SpvId value : values) {
1460*c8dee2aaSAndroid Build Coastguard Worker         words.push_back(value);
1461*c8dee2aaSAndroid Build Coastguard Worker     }
1462*c8dee2aaSAndroid Build Coastguard Worker 
1463*c8dee2aaSAndroid Build Coastguard Worker     return this->writeInstruction(SpvOpCompositeConstruct, words, out);
1464*c8dee2aaSAndroid Build Coastguard Worker }
1465*c8dee2aaSAndroid Build Coastguard Worker 
resultTypeForInstruction(const Instruction & instr)1466*c8dee2aaSAndroid Build Coastguard Worker SPIRVCodeGenerator::Instruction* SPIRVCodeGenerator::resultTypeForInstruction(
1467*c8dee2aaSAndroid Build Coastguard Worker         const Instruction& instr) {
1468*c8dee2aaSAndroid Build Coastguard Worker     // This list should contain every op that we cache that has a result and result-type.
1469*c8dee2aaSAndroid Build Coastguard Worker     // (If one is missing, we will not find some optimization opportunities.)
1470*c8dee2aaSAndroid Build Coastguard Worker     // Generally, the result type of an op is in the 0th word, but I'm not sure if this is
1471*c8dee2aaSAndroid Build Coastguard Worker     // universally true, so it's configurable on a per-op basis.
1472*c8dee2aaSAndroid Build Coastguard Worker     int resultTypeWord;
1473*c8dee2aaSAndroid Build Coastguard Worker     switch (instr.fOp) {
1474*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstant:
1475*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstantTrue:
1476*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstantFalse:
1477*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpConstantComposite:
1478*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpCompositeConstruct:
1479*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpCompositeExtract:
1480*c8dee2aaSAndroid Build Coastguard Worker         case SpvOpLoad:
1481*c8dee2aaSAndroid Build Coastguard Worker             resultTypeWord = 0;
1482*c8dee2aaSAndroid Build Coastguard Worker             break;
1483*c8dee2aaSAndroid Build Coastguard Worker 
1484*c8dee2aaSAndroid Build Coastguard Worker         default:
1485*c8dee2aaSAndroid Build Coastguard Worker             return nullptr;
1486*c8dee2aaSAndroid Build Coastguard Worker     }
1487*c8dee2aaSAndroid Build Coastguard Worker 
1488*c8dee2aaSAndroid Build Coastguard Worker     Instruction* typeInstr = fSpvIdCache.find(instr.fWords[resultTypeWord]);
1489*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(typeInstr);
1490*c8dee2aaSAndroid Build Coastguard Worker     return typeInstr;
1491*c8dee2aaSAndroid Build Coastguard Worker }
1492*c8dee2aaSAndroid Build Coastguard Worker 
numComponentsForVecInstruction(const Instruction & instr)1493*c8dee2aaSAndroid Build Coastguard Worker int SPIRVCodeGenerator::numComponentsForVecInstruction(const Instruction& instr) {
1494*c8dee2aaSAndroid Build Coastguard Worker     // If an instruction is in the op cache, its type should be as well.
1495*c8dee2aaSAndroid Build Coastguard Worker     Instruction* typeInstr = this->resultTypeForInstruction(instr);
1496*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(typeInstr);
1497*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(typeInstr->fOp == SpvOpTypeVector || typeInstr->fOp == SpvOpTypeFloat ||
1498*c8dee2aaSAndroid Build Coastguard Worker              typeInstr->fOp == SpvOpTypeInt || typeInstr->fOp == SpvOpTypeBool);
1499*c8dee2aaSAndroid Build Coastguard Worker 
1500*c8dee2aaSAndroid Build Coastguard Worker     // For vectors, extract their column count. Scalars have one component by definition.
1501*c8dee2aaSAndroid Build Coastguard Worker     //   SpvOpTypeVector ResultID ComponentType NumComponents
1502*c8dee2aaSAndroid Build Coastguard Worker     return (typeInstr->fOp == SpvOpTypeVector) ? typeInstr->fWords[2]
1503*c8dee2aaSAndroid Build Coastguard Worker                                                : 1;
1504*c8dee2aaSAndroid Build Coastguard Worker }
1505*c8dee2aaSAndroid Build Coastguard Worker 
toComponent(SpvId id,int component)1506*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::toComponent(SpvId id, int component) {
1507*c8dee2aaSAndroid Build Coastguard Worker     Instruction* instr = fSpvIdCache.find(id);
1508*c8dee2aaSAndroid Build Coastguard Worker     if (!instr) {
1509*c8dee2aaSAndroid Build Coastguard Worker         return NA;
1510*c8dee2aaSAndroid Build Coastguard Worker     }
1511*c8dee2aaSAndroid Build Coastguard Worker     if (instr->fOp == SpvOpConstantComposite) {
1512*c8dee2aaSAndroid Build Coastguard Worker         // SpvOpConstantComposite ResultType ResultID [components...]
1513*c8dee2aaSAndroid Build Coastguard Worker         // Add 2 to the component index to skip past ResultType and ResultID.
1514*c8dee2aaSAndroid Build Coastguard Worker         return instr->fWords[2 + component];
1515*c8dee2aaSAndroid Build Coastguard Worker     }
1516*c8dee2aaSAndroid Build Coastguard Worker     if (instr->fOp == SpvOpCompositeConstruct) {
1517*c8dee2aaSAndroid Build Coastguard Worker         // SpvOpCompositeConstruct ResultType ResultID [components...]
1518*c8dee2aaSAndroid Build Coastguard Worker         // Vectors have special rules; check to see if we are composing a vector.
1519*c8dee2aaSAndroid Build Coastguard Worker         Instruction* composedType = fSpvIdCache.find(instr->fWords[0]);
1520*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(composedType);
1521*c8dee2aaSAndroid Build Coastguard Worker 
1522*c8dee2aaSAndroid Build Coastguard Worker         // When composing a non-vector, each instruction word maps 1:1 to the component index.
1523*c8dee2aaSAndroid Build Coastguard Worker         // We can just extract out the associated component directly.
1524*c8dee2aaSAndroid Build Coastguard Worker         if (composedType->fOp != SpvOpTypeVector) {
1525*c8dee2aaSAndroid Build Coastguard Worker             return instr->fWords[2 + component];
1526*c8dee2aaSAndroid Build Coastguard Worker         }
1527*c8dee2aaSAndroid Build Coastguard Worker 
1528*c8dee2aaSAndroid Build Coastguard Worker         // When composing a vector, components can be either scalars or vectors.
1529*c8dee2aaSAndroid Build Coastguard Worker         // This means we need to check the op type on each component. (+2 to skip ResultType/Result)
1530*c8dee2aaSAndroid Build Coastguard Worker         for (int index = 2; index < instr->fWords.size(); ++index) {
1531*c8dee2aaSAndroid Build Coastguard Worker             int32_t currentWord = instr->fWords[index];
1532*c8dee2aaSAndroid Build Coastguard Worker 
1533*c8dee2aaSAndroid Build Coastguard Worker             // Retrieve the sub-instruction pointed to by OpCompositeConstruct.
1534*c8dee2aaSAndroid Build Coastguard Worker             Instruction* subinstr = fSpvIdCache.find(currentWord);
1535*c8dee2aaSAndroid Build Coastguard Worker             if (!subinstr) {
1536*c8dee2aaSAndroid Build Coastguard Worker                 return NA;
1537*c8dee2aaSAndroid Build Coastguard Worker             }
1538*c8dee2aaSAndroid Build Coastguard Worker             // If this subinstruction contains the component we're looking for...
1539*c8dee2aaSAndroid Build Coastguard Worker             int numComponents = this->numComponentsForVecInstruction(*subinstr);
1540*c8dee2aaSAndroid Build Coastguard Worker             if (component < numComponents) {
1541*c8dee2aaSAndroid Build Coastguard Worker                 if (numComponents == 1) {
1542*c8dee2aaSAndroid Build Coastguard Worker                     // ... it's a scalar. Return it.
1543*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(component == 0);
1544*c8dee2aaSAndroid Build Coastguard Worker                     return currentWord;
1545*c8dee2aaSAndroid Build Coastguard Worker                 } else {
1546*c8dee2aaSAndroid Build Coastguard Worker                     // ... it's a vector. Recurse into it.
1547*c8dee2aaSAndroid Build Coastguard Worker                     return this->toComponent(currentWord, component);
1548*c8dee2aaSAndroid Build Coastguard Worker                 }
1549*c8dee2aaSAndroid Build Coastguard Worker             }
1550*c8dee2aaSAndroid Build Coastguard Worker             // This sub-instruction doesn't contain our component. Keep walking forward.
1551*c8dee2aaSAndroid Build Coastguard Worker             component -= numComponents;
1552*c8dee2aaSAndroid Build Coastguard Worker         }
1553*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGFAIL("component index goes past the end of this composite value");
1554*c8dee2aaSAndroid Build Coastguard Worker         return NA;
1555*c8dee2aaSAndroid Build Coastguard Worker     }
1556*c8dee2aaSAndroid Build Coastguard Worker     return NA;
1557*c8dee2aaSAndroid Build Coastguard Worker }
1558*c8dee2aaSAndroid Build Coastguard Worker 
writeOpCompositeExtract(const Type & type,SpvId base,int component,OutputStream & out)1559*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeOpCompositeExtract(const Type& type,
1560*c8dee2aaSAndroid Build Coastguard Worker                                                   SpvId base,
1561*c8dee2aaSAndroid Build Coastguard Worker                                                   int component,
1562*c8dee2aaSAndroid Build Coastguard Worker                                                   OutputStream& out) {
1563*c8dee2aaSAndroid Build Coastguard Worker     // If the base op is a composite, we can extract from it directly.
1564*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->toComponent(base, component);
1565*c8dee2aaSAndroid Build Coastguard Worker     if (result != NA) {
1566*c8dee2aaSAndroid Build Coastguard Worker         return result;
1567*c8dee2aaSAndroid Build Coastguard Worker     }
1568*c8dee2aaSAndroid Build Coastguard Worker     return this->writeInstruction(
1569*c8dee2aaSAndroid Build Coastguard Worker             SpvOpCompositeExtract,
1570*c8dee2aaSAndroid Build Coastguard Worker             {this->getType(type), Word::Result(type), base, Word::Number(component)},
1571*c8dee2aaSAndroid Build Coastguard Worker             out);
1572*c8dee2aaSAndroid Build Coastguard Worker }
1573*c8dee2aaSAndroid Build Coastguard Worker 
writeOpCompositeExtract(const Type & type,SpvId base,int componentA,int componentB,OutputStream & out)1574*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeOpCompositeExtract(const Type& type,
1575*c8dee2aaSAndroid Build Coastguard Worker                                                   SpvId base,
1576*c8dee2aaSAndroid Build Coastguard Worker                                                   int componentA,
1577*c8dee2aaSAndroid Build Coastguard Worker                                                   int componentB,
1578*c8dee2aaSAndroid Build Coastguard Worker                                                   OutputStream& out) {
1579*c8dee2aaSAndroid Build Coastguard Worker     // If the base op is a composite, we can extract from it directly.
1580*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->toComponent(base, componentA);
1581*c8dee2aaSAndroid Build Coastguard Worker     if (result != NA) {
1582*c8dee2aaSAndroid Build Coastguard Worker         return this->writeOpCompositeExtract(type, result, componentB, out);
1583*c8dee2aaSAndroid Build Coastguard Worker     }
1584*c8dee2aaSAndroid Build Coastguard Worker     return this->writeInstruction(SpvOpCompositeExtract,
1585*c8dee2aaSAndroid Build Coastguard Worker                                   {this->getType(type),
1586*c8dee2aaSAndroid Build Coastguard Worker                                    Word::Result(type),
1587*c8dee2aaSAndroid Build Coastguard Worker                                    base,
1588*c8dee2aaSAndroid Build Coastguard Worker                                    Word::Number(componentA),
1589*c8dee2aaSAndroid Build Coastguard Worker                                    Word::Number(componentB)},
1590*c8dee2aaSAndroid Build Coastguard Worker                                   out);
1591*c8dee2aaSAndroid Build Coastguard Worker }
1592*c8dee2aaSAndroid Build Coastguard Worker 
writeCapabilities(OutputStream & out)1593*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeCapabilities(OutputStream& out) {
1594*c8dee2aaSAndroid Build Coastguard Worker     for (uint64_t i = 0, bit = 1; i <= kLast_Capability; i++, bit <<= 1) {
1595*c8dee2aaSAndroid Build Coastguard Worker         if (fCapabilities & bit) {
1596*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpCapability, (SpvId) i, out);
1597*c8dee2aaSAndroid Build Coastguard Worker         }
1598*c8dee2aaSAndroid Build Coastguard Worker     }
1599*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpCapability, SpvCapabilityShader, out);
1600*c8dee2aaSAndroid Build Coastguard Worker }
1601*c8dee2aaSAndroid Build Coastguard Worker 
nextId(const Type * type)1602*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::nextId(const Type* type) {
1603*c8dee2aaSAndroid Build Coastguard Worker     return this->nextId(type && type->hasPrecision() && !type->highPrecision()
1604*c8dee2aaSAndroid Build Coastguard Worker                 ? Precision::kRelaxed
1605*c8dee2aaSAndroid Build Coastguard Worker                 : Precision::kDefault);
1606*c8dee2aaSAndroid Build Coastguard Worker }
1607*c8dee2aaSAndroid Build Coastguard Worker 
nextId(Precision precision)1608*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::nextId(Precision precision) {
1609*c8dee2aaSAndroid Build Coastguard Worker     if (precision == Precision::kRelaxed && !fProgram.fConfig->fSettings.fForceHighPrecision) {
1610*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpDecorate, fIdCount, SpvDecorationRelaxedPrecision,
1611*c8dee2aaSAndroid Build Coastguard Worker                                fDecorationBuffer);
1612*c8dee2aaSAndroid Build Coastguard Worker     }
1613*c8dee2aaSAndroid Build Coastguard Worker     return fIdCount++;
1614*c8dee2aaSAndroid Build Coastguard Worker }
1615*c8dee2aaSAndroid Build Coastguard Worker 
writeStruct(const Type & type,const MemoryLayout & memoryLayout)1616*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeStruct(const Type& type, const MemoryLayout& memoryLayout) {
1617*c8dee2aaSAndroid Build Coastguard Worker     // If we've already written out this struct, return its existing SpvId.
1618*c8dee2aaSAndroid Build Coastguard Worker     if (SpvId* cachedStructId = fStructMap.find(&type)) {
1619*c8dee2aaSAndroid Build Coastguard Worker         return *cachedStructId;
1620*c8dee2aaSAndroid Build Coastguard Worker     }
1621*c8dee2aaSAndroid Build Coastguard Worker 
1622*c8dee2aaSAndroid Build Coastguard Worker     // Write all of the field types first, so we don't inadvertently write them while we're in the
1623*c8dee2aaSAndroid Build Coastguard Worker     // middle of writing the struct instruction.
1624*c8dee2aaSAndroid Build Coastguard Worker     Words words;
1625*c8dee2aaSAndroid Build Coastguard Worker     words.push_back(Word::UniqueResult());
1626*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& f : type.fields()) {
1627*c8dee2aaSAndroid Build Coastguard Worker         words.push_back(this->getType(*f.fType, f.fLayout, memoryLayout));
1628*c8dee2aaSAndroid Build Coastguard Worker     }
1629*c8dee2aaSAndroid Build Coastguard Worker     SpvId resultId = this->writeInstruction(SpvOpTypeStruct, words, fConstantBuffer);
1630*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpName, resultId, type.name(), fNameBuffer);
1631*c8dee2aaSAndroid Build Coastguard Worker     fStructMap.set(&type, resultId);
1632*c8dee2aaSAndroid Build Coastguard Worker 
1633*c8dee2aaSAndroid Build Coastguard Worker     size_t offset = 0;
1634*c8dee2aaSAndroid Build Coastguard Worker     for (int32_t i = 0; i < (int32_t) type.fields().size(); i++) {
1635*c8dee2aaSAndroid Build Coastguard Worker         const Field& field = type.fields()[i];
1636*c8dee2aaSAndroid Build Coastguard Worker         if (!memoryLayout.isSupported(*field.fType)) {
1637*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(type.fPosition, "type '" + field.fType->displayName() +
1638*c8dee2aaSAndroid Build Coastguard Worker                                                     "' is not permitted here");
1639*c8dee2aaSAndroid Build Coastguard Worker             return resultId;
1640*c8dee2aaSAndroid Build Coastguard Worker         }
1641*c8dee2aaSAndroid Build Coastguard Worker         size_t size = memoryLayout.size(*field.fType);
1642*c8dee2aaSAndroid Build Coastguard Worker         size_t alignment = memoryLayout.alignment(*field.fType);
1643*c8dee2aaSAndroid Build Coastguard Worker         const Layout& fieldLayout = field.fLayout;
1644*c8dee2aaSAndroid Build Coastguard Worker         if (fieldLayout.fOffset >= 0) {
1645*c8dee2aaSAndroid Build Coastguard Worker             if (fieldLayout.fOffset < (int) offset) {
1646*c8dee2aaSAndroid Build Coastguard Worker                 fContext.fErrors->error(field.fPosition, "offset of field '" +
1647*c8dee2aaSAndroid Build Coastguard Worker                         std::string(field.fName) + "' must be at least " + std::to_string(offset));
1648*c8dee2aaSAndroid Build Coastguard Worker             }
1649*c8dee2aaSAndroid Build Coastguard Worker             if (fieldLayout.fOffset % alignment) {
1650*c8dee2aaSAndroid Build Coastguard Worker                 fContext.fErrors->error(field.fPosition,
1651*c8dee2aaSAndroid Build Coastguard Worker                                         "offset of field '" + std::string(field.fName) +
1652*c8dee2aaSAndroid Build Coastguard Worker                                         "' must be a multiple of " + std::to_string(alignment));
1653*c8dee2aaSAndroid Build Coastguard Worker             }
1654*c8dee2aaSAndroid Build Coastguard Worker             offset = fieldLayout.fOffset;
1655*c8dee2aaSAndroid Build Coastguard Worker         } else {
1656*c8dee2aaSAndroid Build Coastguard Worker             size_t mod = offset % alignment;
1657*c8dee2aaSAndroid Build Coastguard Worker             if (mod) {
1658*c8dee2aaSAndroid Build Coastguard Worker                 offset += alignment - mod;
1659*c8dee2aaSAndroid Build Coastguard Worker             }
1660*c8dee2aaSAndroid Build Coastguard Worker         }
1661*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpMemberName, resultId, i, field.fName, fNameBuffer);
1662*c8dee2aaSAndroid Build Coastguard Worker         this->writeFieldLayout(fieldLayout, resultId, i);
1663*c8dee2aaSAndroid Build Coastguard Worker         if (field.fLayout.fBuiltin < 0) {
1664*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpMemberDecorate, resultId, (SpvId) i, SpvDecorationOffset,
1665*c8dee2aaSAndroid Build Coastguard Worker                                    (SpvId) offset, fDecorationBuffer);
1666*c8dee2aaSAndroid Build Coastguard Worker         }
1667*c8dee2aaSAndroid Build Coastguard Worker         if (field.fType->isMatrix()) {
1668*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpMemberDecorate, resultId, i, SpvDecorationColMajor,
1669*c8dee2aaSAndroid Build Coastguard Worker                                    fDecorationBuffer);
1670*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpMemberDecorate, resultId, i, SpvDecorationMatrixStride,
1671*c8dee2aaSAndroid Build Coastguard Worker                                    (SpvId) memoryLayout.stride(*field.fType),
1672*c8dee2aaSAndroid Build Coastguard Worker                                    fDecorationBuffer);
1673*c8dee2aaSAndroid Build Coastguard Worker         }
1674*c8dee2aaSAndroid Build Coastguard Worker         if (!field.fType->highPrecision()) {
1675*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpMemberDecorate, resultId, (SpvId) i,
1676*c8dee2aaSAndroid Build Coastguard Worker                                    SpvDecorationRelaxedPrecision, fDecorationBuffer);
1677*c8dee2aaSAndroid Build Coastguard Worker         }
1678*c8dee2aaSAndroid Build Coastguard Worker         offset += size;
1679*c8dee2aaSAndroid Build Coastguard Worker         if ((field.fType->isArray() || field.fType->isStruct()) && offset % alignment != 0) {
1680*c8dee2aaSAndroid Build Coastguard Worker             offset += alignment - offset % alignment;
1681*c8dee2aaSAndroid Build Coastguard Worker         }
1682*c8dee2aaSAndroid Build Coastguard Worker     }
1683*c8dee2aaSAndroid Build Coastguard Worker 
1684*c8dee2aaSAndroid Build Coastguard Worker     return resultId;
1685*c8dee2aaSAndroid Build Coastguard Worker }
1686*c8dee2aaSAndroid Build Coastguard Worker 
getType(const Type & type)1687*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::getType(const Type& type) {
1688*c8dee2aaSAndroid Build Coastguard Worker     return this->getType(type, kDefaultTypeLayout, fDefaultMemoryLayout);
1689*c8dee2aaSAndroid Build Coastguard Worker }
1690*c8dee2aaSAndroid Build Coastguard Worker 
layout_flags_to_image_format(LayoutFlags flags)1691*c8dee2aaSAndroid Build Coastguard Worker static SpvImageFormat layout_flags_to_image_format(LayoutFlags flags) {
1692*c8dee2aaSAndroid Build Coastguard Worker     flags &= LayoutFlag::kAllPixelFormats;
1693*c8dee2aaSAndroid Build Coastguard Worker     switch (flags.value()) {
1694*c8dee2aaSAndroid Build Coastguard Worker         case (int)LayoutFlag::kRGBA8:
1695*c8dee2aaSAndroid Build Coastguard Worker             return SpvImageFormatRgba8;
1696*c8dee2aaSAndroid Build Coastguard Worker 
1697*c8dee2aaSAndroid Build Coastguard Worker         case (int)LayoutFlag::kRGBA32F:
1698*c8dee2aaSAndroid Build Coastguard Worker             return SpvImageFormatRgba32f;
1699*c8dee2aaSAndroid Build Coastguard Worker 
1700*c8dee2aaSAndroid Build Coastguard Worker         case (int)LayoutFlag::kR32F:
1701*c8dee2aaSAndroid Build Coastguard Worker             return SpvImageFormatR32f;
1702*c8dee2aaSAndroid Build Coastguard Worker 
1703*c8dee2aaSAndroid Build Coastguard Worker         default:
1704*c8dee2aaSAndroid Build Coastguard Worker             return SpvImageFormatUnknown;
1705*c8dee2aaSAndroid Build Coastguard Worker     }
1706*c8dee2aaSAndroid Build Coastguard Worker 
1707*c8dee2aaSAndroid Build Coastguard Worker     SkUNREACHABLE;
1708*c8dee2aaSAndroid Build Coastguard Worker }
1709*c8dee2aaSAndroid Build Coastguard Worker 
getType(const Type & rawType,const Layout & typeLayout,const MemoryLayout & memoryLayout)1710*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::getType(const Type& rawType,
1711*c8dee2aaSAndroid Build Coastguard Worker                                   const Layout& typeLayout,
1712*c8dee2aaSAndroid Build Coastguard Worker                                   const MemoryLayout& memoryLayout) {
1713*c8dee2aaSAndroid Build Coastguard Worker     const Type* type = &rawType;
1714*c8dee2aaSAndroid Build Coastguard Worker 
1715*c8dee2aaSAndroid Build Coastguard Worker     switch (type->typeKind()) {
1716*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kVoid: {
1717*c8dee2aaSAndroid Build Coastguard Worker             return this->writeInstruction(SpvOpTypeVoid, Words{Word::Result()}, fConstantBuffer);
1718*c8dee2aaSAndroid Build Coastguard Worker         }
1719*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kScalar:
1720*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kLiteral: {
1721*c8dee2aaSAndroid Build Coastguard Worker             if (type->isBoolean()) {
1722*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeInstruction(SpvOpTypeBool, {Word::Result()}, fConstantBuffer);
1723*c8dee2aaSAndroid Build Coastguard Worker             }
1724*c8dee2aaSAndroid Build Coastguard Worker             if (type->isSigned()) {
1725*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeInstruction(
1726*c8dee2aaSAndroid Build Coastguard Worker                         SpvOpTypeInt,
1727*c8dee2aaSAndroid Build Coastguard Worker                         Words{Word::Result(), Word::Number(32), Word::Number(1)},
1728*c8dee2aaSAndroid Build Coastguard Worker                         fConstantBuffer);
1729*c8dee2aaSAndroid Build Coastguard Worker             }
1730*c8dee2aaSAndroid Build Coastguard Worker             if (type->isUnsigned()) {
1731*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeInstruction(
1732*c8dee2aaSAndroid Build Coastguard Worker                         SpvOpTypeInt,
1733*c8dee2aaSAndroid Build Coastguard Worker                         Words{Word::Result(), Word::Number(32), Word::Number(0)},
1734*c8dee2aaSAndroid Build Coastguard Worker                         fConstantBuffer);
1735*c8dee2aaSAndroid Build Coastguard Worker             }
1736*c8dee2aaSAndroid Build Coastguard Worker             if (type->isFloat()) {
1737*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeInstruction(
1738*c8dee2aaSAndroid Build Coastguard Worker                         SpvOpTypeFloat,
1739*c8dee2aaSAndroid Build Coastguard Worker                         Words{Word::Result(), Word::Number(32)},
1740*c8dee2aaSAndroid Build Coastguard Worker                         fConstantBuffer);
1741*c8dee2aaSAndroid Build Coastguard Worker             }
1742*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAILF("unrecognized scalar type '%s'", type->description().c_str());
1743*c8dee2aaSAndroid Build Coastguard Worker             return NA;
1744*c8dee2aaSAndroid Build Coastguard Worker         }
1745*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kVector: {
1746*c8dee2aaSAndroid Build Coastguard Worker             SpvId scalarTypeId = this->getType(type->componentType(), typeLayout, memoryLayout);
1747*c8dee2aaSAndroid Build Coastguard Worker             return this->writeInstruction(
1748*c8dee2aaSAndroid Build Coastguard Worker                     SpvOpTypeVector,
1749*c8dee2aaSAndroid Build Coastguard Worker                     Words{Word::Result(), scalarTypeId, Word::Number(type->columns())},
1750*c8dee2aaSAndroid Build Coastguard Worker                     fConstantBuffer);
1751*c8dee2aaSAndroid Build Coastguard Worker         }
1752*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kMatrix: {
1753*c8dee2aaSAndroid Build Coastguard Worker             SpvId vectorTypeId = this->getType(IndexExpression::IndexType(fContext, *type),
1754*c8dee2aaSAndroid Build Coastguard Worker                                                typeLayout,
1755*c8dee2aaSAndroid Build Coastguard Worker                                                memoryLayout);
1756*c8dee2aaSAndroid Build Coastguard Worker             return this->writeInstruction(
1757*c8dee2aaSAndroid Build Coastguard Worker                     SpvOpTypeMatrix,
1758*c8dee2aaSAndroid Build Coastguard Worker                     Words{Word::Result(), vectorTypeId, Word::Number(type->columns())},
1759*c8dee2aaSAndroid Build Coastguard Worker                     fConstantBuffer);
1760*c8dee2aaSAndroid Build Coastguard Worker         }
1761*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kArray: {
1762*c8dee2aaSAndroid Build Coastguard Worker             const MemoryLayout arrayMemoryLayout =
1763*c8dee2aaSAndroid Build Coastguard Worker                                     fCaps.fForceStd430ArrayLayout
1764*c8dee2aaSAndroid Build Coastguard Worker                                         ? MemoryLayout(MemoryLayout::Standard::k430)
1765*c8dee2aaSAndroid Build Coastguard Worker                                         : memoryLayout;
1766*c8dee2aaSAndroid Build Coastguard Worker 
1767*c8dee2aaSAndroid Build Coastguard Worker             if (!arrayMemoryLayout.isSupported(*type)) {
1768*c8dee2aaSAndroid Build Coastguard Worker                 fContext.fErrors->error(type->fPosition, "type '" + type->displayName() +
1769*c8dee2aaSAndroid Build Coastguard Worker                                                          "' is not permitted here");
1770*c8dee2aaSAndroid Build Coastguard Worker                 return NA;
1771*c8dee2aaSAndroid Build Coastguard Worker             }
1772*c8dee2aaSAndroid Build Coastguard Worker             size_t stride = arrayMemoryLayout.stride(*type);
1773*c8dee2aaSAndroid Build Coastguard Worker             SpvId typeId = this->getType(type->componentType(), typeLayout, arrayMemoryLayout);
1774*c8dee2aaSAndroid Build Coastguard Worker             SpvId result = NA;
1775*c8dee2aaSAndroid Build Coastguard Worker             if (type->isUnsizedArray()) {
1776*c8dee2aaSAndroid Build Coastguard Worker                 result = this->writeInstruction(SpvOpTypeRuntimeArray,
1777*c8dee2aaSAndroid Build Coastguard Worker                                                 Words{Word::KeyedResult(stride), typeId},
1778*c8dee2aaSAndroid Build Coastguard Worker                                                 fConstantBuffer);
1779*c8dee2aaSAndroid Build Coastguard Worker             } else {
1780*c8dee2aaSAndroid Build Coastguard Worker                 SpvId countId = this->writeLiteral(type->columns(), *fContext.fTypes.fInt);
1781*c8dee2aaSAndroid Build Coastguard Worker                 result = this->writeInstruction(SpvOpTypeArray,
1782*c8dee2aaSAndroid Build Coastguard Worker                                                 Words{Word::KeyedResult(stride), typeId, countId},
1783*c8dee2aaSAndroid Build Coastguard Worker                                                 fConstantBuffer);
1784*c8dee2aaSAndroid Build Coastguard Worker             }
1785*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpDecorate,
1786*c8dee2aaSAndroid Build Coastguard Worker                                    {result, SpvDecorationArrayStride, Word::Number(stride)},
1787*c8dee2aaSAndroid Build Coastguard Worker                                    fDecorationBuffer);
1788*c8dee2aaSAndroid Build Coastguard Worker             return result;
1789*c8dee2aaSAndroid Build Coastguard Worker         }
1790*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kStruct: {
1791*c8dee2aaSAndroid Build Coastguard Worker             return this->writeStruct(*type, memoryLayout);
1792*c8dee2aaSAndroid Build Coastguard Worker         }
1793*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kSeparateSampler: {
1794*c8dee2aaSAndroid Build Coastguard Worker             return this->writeInstruction(SpvOpTypeSampler, Words{Word::Result()}, fConstantBuffer);
1795*c8dee2aaSAndroid Build Coastguard Worker         }
1796*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kSampler: {
1797*c8dee2aaSAndroid Build Coastguard Worker             if (SpvDimBuffer == type->dimensions()) {
1798*c8dee2aaSAndroid Build Coastguard Worker                 fCapabilities |= 1ULL << SpvCapabilitySampledBuffer;
1799*c8dee2aaSAndroid Build Coastguard Worker             }
1800*c8dee2aaSAndroid Build Coastguard Worker             SpvId imageTypeId = this->getType(type->textureType(), typeLayout, memoryLayout);
1801*c8dee2aaSAndroid Build Coastguard Worker             return this->writeInstruction(SpvOpTypeSampledImage,
1802*c8dee2aaSAndroid Build Coastguard Worker                                           Words{Word::Result(), imageTypeId},
1803*c8dee2aaSAndroid Build Coastguard Worker                                           fConstantBuffer);
1804*c8dee2aaSAndroid Build Coastguard Worker         }
1805*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kTexture: {
1806*c8dee2aaSAndroid Build Coastguard Worker             SpvId floatTypeId = this->getType(*fContext.fTypes.fFloat,
1807*c8dee2aaSAndroid Build Coastguard Worker                                               kDefaultTypeLayout,
1808*c8dee2aaSAndroid Build Coastguard Worker                                               memoryLayout);
1809*c8dee2aaSAndroid Build Coastguard Worker 
1810*c8dee2aaSAndroid Build Coastguard Worker             bool sampled = (type->textureAccess() == Type::TextureAccess::kSample);
1811*c8dee2aaSAndroid Build Coastguard Worker             SpvImageFormat format = (!sampled && type->dimensions() != SpvDimSubpassData)
1812*c8dee2aaSAndroid Build Coastguard Worker                                             ? layout_flags_to_image_format(typeLayout.fFlags)
1813*c8dee2aaSAndroid Build Coastguard Worker                                             : SpvImageFormatUnknown;
1814*c8dee2aaSAndroid Build Coastguard Worker 
1815*c8dee2aaSAndroid Build Coastguard Worker             return this->writeInstruction(SpvOpTypeImage,
1816*c8dee2aaSAndroid Build Coastguard Worker                                           Words{Word::Result(),
1817*c8dee2aaSAndroid Build Coastguard Worker                                                 floatTypeId,
1818*c8dee2aaSAndroid Build Coastguard Worker                                                 Word::Number(type->dimensions()),
1819*c8dee2aaSAndroid Build Coastguard Worker                                                 Word::Number(type->isDepth()),
1820*c8dee2aaSAndroid Build Coastguard Worker                                                 Word::Number(type->isArrayedTexture()),
1821*c8dee2aaSAndroid Build Coastguard Worker                                                 Word::Number(type->isMultisampled()),
1822*c8dee2aaSAndroid Build Coastguard Worker                                                 Word::Number(sampled ? 1 : 2),
1823*c8dee2aaSAndroid Build Coastguard Worker                                                 format},
1824*c8dee2aaSAndroid Build Coastguard Worker                                           fConstantBuffer);
1825*c8dee2aaSAndroid Build Coastguard Worker         }
1826*c8dee2aaSAndroid Build Coastguard Worker         case Type::TypeKind::kAtomic: {
1827*c8dee2aaSAndroid Build Coastguard Worker             // SkSL currently only supports the atomicUint type.
1828*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(type->matches(*fContext.fTypes.fAtomicUInt));
1829*c8dee2aaSAndroid Build Coastguard Worker             // SPIR-V doesn't have atomic types. Rather, it allows atomic operations on primitive
1830*c8dee2aaSAndroid Build Coastguard Worker             // types. The SPIR-V type of an SkSL atomic is simply the underlying type.
1831*c8dee2aaSAndroid Build Coastguard Worker             return this->writeInstruction(SpvOpTypeInt,
1832*c8dee2aaSAndroid Build Coastguard Worker                                           Words{Word::Result(), Word::Number(32), Word::Number(0)},
1833*c8dee2aaSAndroid Build Coastguard Worker                                           fConstantBuffer);
1834*c8dee2aaSAndroid Build Coastguard Worker         }
1835*c8dee2aaSAndroid Build Coastguard Worker         default: {
1836*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAILF("invalid type: %s", type->description().c_str());
1837*c8dee2aaSAndroid Build Coastguard Worker             return NA;
1838*c8dee2aaSAndroid Build Coastguard Worker         }
1839*c8dee2aaSAndroid Build Coastguard Worker     }
1840*c8dee2aaSAndroid Build Coastguard Worker }
1841*c8dee2aaSAndroid Build Coastguard Worker 
getFunctionType(const FunctionDeclaration & function)1842*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::getFunctionType(const FunctionDeclaration& function) {
1843*c8dee2aaSAndroid Build Coastguard Worker     Words words;
1844*c8dee2aaSAndroid Build Coastguard Worker     words.push_back(Word::Result());
1845*c8dee2aaSAndroid Build Coastguard Worker     words.push_back(this->getType(function.returnType()));
1846*c8dee2aaSAndroid Build Coastguard Worker     for (const Variable* parameter : function.parameters()) {
1847*c8dee2aaSAndroid Build Coastguard Worker         bool paramIsSpecialized = fActiveSpecialization && fActiveSpecialization->find(parameter);
1848*c8dee2aaSAndroid Build Coastguard Worker         if (fUseTextureSamplerPairs && parameter->type().isSampler()) {
1849*c8dee2aaSAndroid Build Coastguard Worker             words.push_back(this->getFunctionParameterType(parameter->type().textureType(),
1850*c8dee2aaSAndroid Build Coastguard Worker                                                            parameter->layout()));
1851*c8dee2aaSAndroid Build Coastguard Worker             if (!paramIsSpecialized) {
1852*c8dee2aaSAndroid Build Coastguard Worker                 words.push_back(this->getFunctionParameterType(*fContext.fTypes.fSampler,
1853*c8dee2aaSAndroid Build Coastguard Worker                                                                kDefaultTypeLayout));
1854*c8dee2aaSAndroid Build Coastguard Worker             }
1855*c8dee2aaSAndroid Build Coastguard Worker         } else if (!paramIsSpecialized) {
1856*c8dee2aaSAndroid Build Coastguard Worker             words.push_back(this->getFunctionParameterType(parameter->type(), parameter->layout()));
1857*c8dee2aaSAndroid Build Coastguard Worker         }
1858*c8dee2aaSAndroid Build Coastguard Worker     }
1859*c8dee2aaSAndroid Build Coastguard Worker     return this->writeInstruction(SpvOpTypeFunction, words, fConstantBuffer);
1860*c8dee2aaSAndroid Build Coastguard Worker }
1861*c8dee2aaSAndroid Build Coastguard Worker 
getFunctionParameterType(const Type & parameterType,const Layout & parameterLayout)1862*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::getFunctionParameterType(const Type& parameterType,
1863*c8dee2aaSAndroid Build Coastguard Worker                                                    const Layout& parameterLayout) {
1864*c8dee2aaSAndroid Build Coastguard Worker     // glslang treats all function arguments as pointers whether they need to be or
1865*c8dee2aaSAndroid Build Coastguard Worker     // not. I was initially puzzled by this until I ran bizarre failures with certain
1866*c8dee2aaSAndroid Build Coastguard Worker     // patterns of function calls and control constructs, as exemplified by this minimal
1867*c8dee2aaSAndroid Build Coastguard Worker     // failure case:
1868*c8dee2aaSAndroid Build Coastguard Worker     //
1869*c8dee2aaSAndroid Build Coastguard Worker     // void sphere(float x) {
1870*c8dee2aaSAndroid Build Coastguard Worker     // }
1871*c8dee2aaSAndroid Build Coastguard Worker     //
1872*c8dee2aaSAndroid Build Coastguard Worker     // void map() {
1873*c8dee2aaSAndroid Build Coastguard Worker     //     sphere(1.0);
1874*c8dee2aaSAndroid Build Coastguard Worker     // }
1875*c8dee2aaSAndroid Build Coastguard Worker     //
1876*c8dee2aaSAndroid Build Coastguard Worker     // void main() {
1877*c8dee2aaSAndroid Build Coastguard Worker     //     for (int i = 0; i < 1; i++) {
1878*c8dee2aaSAndroid Build Coastguard Worker     //         map();
1879*c8dee2aaSAndroid Build Coastguard Worker     //     }
1880*c8dee2aaSAndroid Build Coastguard Worker     // }
1881*c8dee2aaSAndroid Build Coastguard Worker     //
1882*c8dee2aaSAndroid Build Coastguard Worker     // As of this writing, compiling this in the "obvious" way (with sphere taking a float)
1883*c8dee2aaSAndroid Build Coastguard Worker     // crashes. Making it take a float* and storing the argument in a temporary variable,
1884*c8dee2aaSAndroid Build Coastguard Worker     // as glslang does, fixes it.
1885*c8dee2aaSAndroid Build Coastguard Worker     //
1886*c8dee2aaSAndroid Build Coastguard Worker     // The consensus among shader compiler authors seems to be that GPU driver generally don't
1887*c8dee2aaSAndroid Build Coastguard Worker     // handle value-based parameters consistently. It is highly likely that they fit their
1888*c8dee2aaSAndroid Build Coastguard Worker     // implementations to conform to glslang. We take care to do so ourselves.
1889*c8dee2aaSAndroid Build Coastguard Worker     //
1890*c8dee2aaSAndroid Build Coastguard Worker     // Our implementation first stores every parameter value into a function storage-class pointer
1891*c8dee2aaSAndroid Build Coastguard Worker     // before calling a function. The exception is for opaque handle types (samplers and textures)
1892*c8dee2aaSAndroid Build Coastguard Worker     // which must be stored in a pointer with UniformConstant storage-class. This prevents
1893*c8dee2aaSAndroid Build Coastguard Worker     // unnecessary temporaries (becuase opaque handles are always rooted in a pointer variable),
1894*c8dee2aaSAndroid Build Coastguard Worker     // matches glslang's behavior, and translates into WGSL more easily when targeting Dawn.
1895*c8dee2aaSAndroid Build Coastguard Worker     StorageClass storageClass;
1896*c8dee2aaSAndroid Build Coastguard Worker     if (parameterType.typeKind() == Type::TypeKind::kSampler ||
1897*c8dee2aaSAndroid Build Coastguard Worker         parameterType.typeKind() == Type::TypeKind::kSeparateSampler ||
1898*c8dee2aaSAndroid Build Coastguard Worker         parameterType.typeKind() == Type::TypeKind::kTexture) {
1899*c8dee2aaSAndroid Build Coastguard Worker         storageClass = StorageClass::kUniformConstant;
1900*c8dee2aaSAndroid Build Coastguard Worker     } else {
1901*c8dee2aaSAndroid Build Coastguard Worker         storageClass = StorageClass::kFunction;
1902*c8dee2aaSAndroid Build Coastguard Worker     }
1903*c8dee2aaSAndroid Build Coastguard Worker     return this->getPointerType(parameterType,
1904*c8dee2aaSAndroid Build Coastguard Worker                                 parameterLayout,
1905*c8dee2aaSAndroid Build Coastguard Worker                                 this->memoryLayoutForStorageClass(storageClass),
1906*c8dee2aaSAndroid Build Coastguard Worker                                 storageClass);
1907*c8dee2aaSAndroid Build Coastguard Worker }
1908*c8dee2aaSAndroid Build Coastguard Worker 
getPointerType(const Type & type,StorageClass storageClass)1909*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::getPointerType(const Type& type, StorageClass storageClass) {
1910*c8dee2aaSAndroid Build Coastguard Worker     return this->getPointerType(type,
1911*c8dee2aaSAndroid Build Coastguard Worker                                 kDefaultTypeLayout,
1912*c8dee2aaSAndroid Build Coastguard Worker                                 this->memoryLayoutForStorageClass(storageClass),
1913*c8dee2aaSAndroid Build Coastguard Worker                                 storageClass);
1914*c8dee2aaSAndroid Build Coastguard Worker }
1915*c8dee2aaSAndroid Build Coastguard Worker 
getPointerType(const Type & type,const Layout & typeLayout,const MemoryLayout & memoryLayout,StorageClass storageClass)1916*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::getPointerType(const Type& type,
1917*c8dee2aaSAndroid Build Coastguard Worker                                          const Layout& typeLayout,
1918*c8dee2aaSAndroid Build Coastguard Worker                                          const MemoryLayout& memoryLayout,
1919*c8dee2aaSAndroid Build Coastguard Worker                                          StorageClass storageClass) {
1920*c8dee2aaSAndroid Build Coastguard Worker     return this->writeInstruction(SpvOpTypePointer,
1921*c8dee2aaSAndroid Build Coastguard Worker                                   Words{Word::Result(),
1922*c8dee2aaSAndroid Build Coastguard Worker                                         Word::Number(get_storage_class_spv_id(storageClass)),
1923*c8dee2aaSAndroid Build Coastguard Worker                                         this->getType(type, typeLayout, memoryLayout)},
1924*c8dee2aaSAndroid Build Coastguard Worker                                   fConstantBuffer);
1925*c8dee2aaSAndroid Build Coastguard Worker }
1926*c8dee2aaSAndroid Build Coastguard Worker 
writeExpression(const Expression & expr,OutputStream & out)1927*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeExpression(const Expression& expr, OutputStream& out) {
1928*c8dee2aaSAndroid Build Coastguard Worker     switch (expr.kind()) {
1929*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kBinary:
1930*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryExpression(expr.as<BinaryExpression>(), out);
1931*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorArrayCast:
1932*c8dee2aaSAndroid Build Coastguard Worker             return this->writeExpression(*expr.as<ConstructorArrayCast>().argument(), out);
1933*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorArray:
1934*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorStruct:
1935*c8dee2aaSAndroid Build Coastguard Worker             return this->writeCompositeConstructor(expr.asAnyConstructor(), out);
1936*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorDiagonalMatrix:
1937*c8dee2aaSAndroid Build Coastguard Worker             return this->writeConstructorDiagonalMatrix(expr.as<ConstructorDiagonalMatrix>(), out);
1938*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorMatrixResize:
1939*c8dee2aaSAndroid Build Coastguard Worker             return this->writeConstructorMatrixResize(expr.as<ConstructorMatrixResize>(), out);
1940*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorScalarCast:
1941*c8dee2aaSAndroid Build Coastguard Worker             return this->writeConstructorScalarCast(expr.as<ConstructorScalarCast>(), out);
1942*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorSplat:
1943*c8dee2aaSAndroid Build Coastguard Worker             return this->writeConstructorSplat(expr.as<ConstructorSplat>(), out);
1944*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorCompound:
1945*c8dee2aaSAndroid Build Coastguard Worker             return this->writeConstructorCompound(expr.as<ConstructorCompound>(), out);
1946*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kConstructorCompoundCast:
1947*c8dee2aaSAndroid Build Coastguard Worker             return this->writeConstructorCompoundCast(expr.as<ConstructorCompoundCast>(), out);
1948*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kEmpty:
1949*c8dee2aaSAndroid Build Coastguard Worker             return NA;
1950*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kFieldAccess:
1951*c8dee2aaSAndroid Build Coastguard Worker             return this->writeFieldAccess(expr.as<FieldAccess>(), out);
1952*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kFunctionCall:
1953*c8dee2aaSAndroid Build Coastguard Worker             return this->writeFunctionCall(expr.as<FunctionCall>(), out);
1954*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kLiteral:
1955*c8dee2aaSAndroid Build Coastguard Worker             return this->writeLiteral(expr.as<Literal>());
1956*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kPrefix:
1957*c8dee2aaSAndroid Build Coastguard Worker             return this->writePrefixExpression(expr.as<PrefixExpression>(), out);
1958*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kPostfix:
1959*c8dee2aaSAndroid Build Coastguard Worker             return this->writePostfixExpression(expr.as<PostfixExpression>(), out);
1960*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kSwizzle:
1961*c8dee2aaSAndroid Build Coastguard Worker             return this->writeSwizzle(expr.as<Swizzle>(), out);
1962*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kVariableReference:
1963*c8dee2aaSAndroid Build Coastguard Worker             return this->writeVariableReference(expr.as<VariableReference>(), out);
1964*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kTernary:
1965*c8dee2aaSAndroid Build Coastguard Worker             return this->writeTernaryExpression(expr.as<TernaryExpression>(), out);
1966*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kIndex:
1967*c8dee2aaSAndroid Build Coastguard Worker             return this->writeIndexExpression(expr.as<IndexExpression>(), out);
1968*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kSetting:
1969*c8dee2aaSAndroid Build Coastguard Worker             return this->writeExpression(*expr.as<Setting>().toLiteral(fCaps), out);
1970*c8dee2aaSAndroid Build Coastguard Worker         default:
1971*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAILF("unsupported expression: %s", expr.description().c_str());
1972*c8dee2aaSAndroid Build Coastguard Worker             break;
1973*c8dee2aaSAndroid Build Coastguard Worker     }
1974*c8dee2aaSAndroid Build Coastguard Worker     return NA;
1975*c8dee2aaSAndroid Build Coastguard Worker }
1976*c8dee2aaSAndroid Build Coastguard Worker 
writeIntrinsicCall(const FunctionCall & c,OutputStream & out)1977*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeIntrinsicCall(const FunctionCall& c, OutputStream& out) {
1978*c8dee2aaSAndroid Build Coastguard Worker     const FunctionDeclaration& function = c.function();
1979*c8dee2aaSAndroid Build Coastguard Worker     Intrinsic intrinsic = this->getIntrinsic(function.intrinsicKind());
1980*c8dee2aaSAndroid Build Coastguard Worker     if (intrinsic.opKind == kInvalid_IntrinsicOpcodeKind) {
1981*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(c.fPosition, "unsupported intrinsic '" + function.description() +
1982*c8dee2aaSAndroid Build Coastguard Worker                 "'");
1983*c8dee2aaSAndroid Build Coastguard Worker         return NA;
1984*c8dee2aaSAndroid Build Coastguard Worker     }
1985*c8dee2aaSAndroid Build Coastguard Worker     const ExpressionArray& arguments = c.arguments();
1986*c8dee2aaSAndroid Build Coastguard Worker     int32_t intrinsicId = intrinsic.floatOp;
1987*c8dee2aaSAndroid Build Coastguard Worker     if (!arguments.empty()) {
1988*c8dee2aaSAndroid Build Coastguard Worker         const Type& type = arguments[0]->type();
1989*c8dee2aaSAndroid Build Coastguard Worker         if (intrinsic.opKind == kSpecial_IntrinsicOpcodeKind) {
1990*c8dee2aaSAndroid Build Coastguard Worker             // Keep the default float op.
1991*c8dee2aaSAndroid Build Coastguard Worker         } else {
1992*c8dee2aaSAndroid Build Coastguard Worker             intrinsicId = pick_by_type(type, intrinsic.floatOp, intrinsic.signedOp,
1993*c8dee2aaSAndroid Build Coastguard Worker                                        intrinsic.unsignedOp, intrinsic.boolOp);
1994*c8dee2aaSAndroid Build Coastguard Worker         }
1995*c8dee2aaSAndroid Build Coastguard Worker     }
1996*c8dee2aaSAndroid Build Coastguard Worker     switch (intrinsic.opKind) {
1997*c8dee2aaSAndroid Build Coastguard Worker         case kGLSL_STD_450_IntrinsicOpcodeKind: {
1998*c8dee2aaSAndroid Build Coastguard Worker             SpvId result = this->nextId(&c.type());
1999*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> argumentIds;
2000*c8dee2aaSAndroid Build Coastguard Worker             argumentIds.reserve_exact(arguments.size());
2001*c8dee2aaSAndroid Build Coastguard Worker             std::vector<TempVar> tempVars;
2002*c8dee2aaSAndroid Build Coastguard Worker             for (int i = 0; i < arguments.size(); i++) {
2003*c8dee2aaSAndroid Build Coastguard Worker                 this->writeFunctionCallArgument(argumentIds, c, i, &tempVars,
2004*c8dee2aaSAndroid Build Coastguard Worker                                                 /*specializedParams=*/nullptr, out);
2005*c8dee2aaSAndroid Build Coastguard Worker             }
2006*c8dee2aaSAndroid Build Coastguard Worker             this->writeOpCode(SpvOpExtInst, 5 + (int32_t) argumentIds.size(), out);
2007*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(this->getType(c.type()), out);
2008*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(result, out);
2009*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(fGLSLExtendedInstructions, out);
2010*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(intrinsicId, out);
2011*c8dee2aaSAndroid Build Coastguard Worker             for (SpvId id : argumentIds) {
2012*c8dee2aaSAndroid Build Coastguard Worker                 this->writeWord(id, out);
2013*c8dee2aaSAndroid Build Coastguard Worker             }
2014*c8dee2aaSAndroid Build Coastguard Worker             this->copyBackTempVars(tempVars, out);
2015*c8dee2aaSAndroid Build Coastguard Worker             return result;
2016*c8dee2aaSAndroid Build Coastguard Worker         }
2017*c8dee2aaSAndroid Build Coastguard Worker         case kSPIRV_IntrinsicOpcodeKind: {
2018*c8dee2aaSAndroid Build Coastguard Worker             // GLSL supports dot(float, float), but SPIR-V does not. Convert it to FMul
2019*c8dee2aaSAndroid Build Coastguard Worker             if (intrinsicId == SpvOpDot && arguments[0]->type().isScalar()) {
2020*c8dee2aaSAndroid Build Coastguard Worker                 intrinsicId = SpvOpFMul;
2021*c8dee2aaSAndroid Build Coastguard Worker             }
2022*c8dee2aaSAndroid Build Coastguard Worker             SpvId result = this->nextId(&c.type());
2023*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> argumentIds;
2024*c8dee2aaSAndroid Build Coastguard Worker             argumentIds.reserve_exact(arguments.size());
2025*c8dee2aaSAndroid Build Coastguard Worker             std::vector<TempVar> tempVars;
2026*c8dee2aaSAndroid Build Coastguard Worker             for (int i = 0; i < arguments.size(); i++) {
2027*c8dee2aaSAndroid Build Coastguard Worker                 this->writeFunctionCallArgument(argumentIds, c, i, &tempVars,
2028*c8dee2aaSAndroid Build Coastguard Worker                                                 /*specializedParams=*/nullptr, out);
2029*c8dee2aaSAndroid Build Coastguard Worker             }
2030*c8dee2aaSAndroid Build Coastguard Worker             if (!c.type().isVoid()) {
2031*c8dee2aaSAndroid Build Coastguard Worker                 this->writeOpCode((SpvOp_) intrinsicId, 3 + (int32_t) arguments.size(), out);
2032*c8dee2aaSAndroid Build Coastguard Worker                 this->writeWord(this->getType(c.type()), out);
2033*c8dee2aaSAndroid Build Coastguard Worker                 this->writeWord(result, out);
2034*c8dee2aaSAndroid Build Coastguard Worker             } else {
2035*c8dee2aaSAndroid Build Coastguard Worker                 this->writeOpCode((SpvOp_) intrinsicId, 1 + (int32_t) arguments.size(), out);
2036*c8dee2aaSAndroid Build Coastguard Worker             }
2037*c8dee2aaSAndroid Build Coastguard Worker             for (SpvId id : argumentIds) {
2038*c8dee2aaSAndroid Build Coastguard Worker                 this->writeWord(id, out);
2039*c8dee2aaSAndroid Build Coastguard Worker             }
2040*c8dee2aaSAndroid Build Coastguard Worker             this->copyBackTempVars(tempVars, out);
2041*c8dee2aaSAndroid Build Coastguard Worker             return result;
2042*c8dee2aaSAndroid Build Coastguard Worker         }
2043*c8dee2aaSAndroid Build Coastguard Worker         case kSpecial_IntrinsicOpcodeKind:
2044*c8dee2aaSAndroid Build Coastguard Worker             return this->writeSpecialIntrinsic(c, (SpecialIntrinsic) intrinsicId, out);
2045*c8dee2aaSAndroid Build Coastguard Worker         default:
2046*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(c.fPosition, "unsupported intrinsic '" +
2047*c8dee2aaSAndroid Build Coastguard Worker                     function.description() + "'");
2048*c8dee2aaSAndroid Build Coastguard Worker             return NA;
2049*c8dee2aaSAndroid Build Coastguard Worker     }
2050*c8dee2aaSAndroid Build Coastguard Worker }
2051*c8dee2aaSAndroid Build Coastguard Worker 
vectorize(const Expression & arg,int vectorSize,OutputStream & out)2052*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::vectorize(const Expression& arg, int vectorSize, OutputStream& out) {
2053*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(vectorSize >= 1 && vectorSize <= 4);
2054*c8dee2aaSAndroid Build Coastguard Worker     const Type& argType = arg.type();
2055*c8dee2aaSAndroid Build Coastguard Worker     if (argType.isScalar() && vectorSize > 1) {
2056*c8dee2aaSAndroid Build Coastguard Worker         SpvId argID = this->writeExpression(arg, out);
2057*c8dee2aaSAndroid Build Coastguard Worker         return this->splat(argType.toCompound(fContext, vectorSize, /*rows=*/1), argID, out);
2058*c8dee2aaSAndroid Build Coastguard Worker     }
2059*c8dee2aaSAndroid Build Coastguard Worker 
2060*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(vectorSize == argType.columns());
2061*c8dee2aaSAndroid Build Coastguard Worker     return this->writeExpression(arg, out);
2062*c8dee2aaSAndroid Build Coastguard Worker }
2063*c8dee2aaSAndroid Build Coastguard Worker 
vectorize(const ExpressionArray & args,OutputStream & out)2064*c8dee2aaSAndroid Build Coastguard Worker TArray<SpvId> SPIRVCodeGenerator::vectorize(const ExpressionArray& args, OutputStream& out) {
2065*c8dee2aaSAndroid Build Coastguard Worker     int vectorSize = 1;
2066*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& a : args) {
2067*c8dee2aaSAndroid Build Coastguard Worker         if (a->type().isVector()) {
2068*c8dee2aaSAndroid Build Coastguard Worker             if (vectorSize > 1) {
2069*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(a->type().columns() == vectorSize);
2070*c8dee2aaSAndroid Build Coastguard Worker             } else {
2071*c8dee2aaSAndroid Build Coastguard Worker                 vectorSize = a->type().columns();
2072*c8dee2aaSAndroid Build Coastguard Worker             }
2073*c8dee2aaSAndroid Build Coastguard Worker         }
2074*c8dee2aaSAndroid Build Coastguard Worker     }
2075*c8dee2aaSAndroid Build Coastguard Worker     TArray<SpvId> result;
2076*c8dee2aaSAndroid Build Coastguard Worker     result.reserve_exact(args.size());
2077*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& arg : args) {
2078*c8dee2aaSAndroid Build Coastguard Worker         result.push_back(this->vectorize(*arg, vectorSize, out));
2079*c8dee2aaSAndroid Build Coastguard Worker     }
2080*c8dee2aaSAndroid Build Coastguard Worker     return result;
2081*c8dee2aaSAndroid Build Coastguard Worker }
2082*c8dee2aaSAndroid Build Coastguard Worker 
writeGLSLExtendedInstruction(const Type & type,SpvId id,SpvId floatInst,SpvId signedInst,SpvId unsignedInst,const TArray<SpvId> & args,OutputStream & out)2083*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeGLSLExtendedInstruction(const Type& type, SpvId id, SpvId floatInst,
2084*c8dee2aaSAndroid Build Coastguard Worker                                                       SpvId signedInst, SpvId unsignedInst,
2085*c8dee2aaSAndroid Build Coastguard Worker                                                       const TArray<SpvId>& args,
2086*c8dee2aaSAndroid Build Coastguard Worker                                                       OutputStream& out) {
2087*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(SpvOpExtInst, 5 + args.size(), out);
2088*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(this->getType(type), out);
2089*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(id, out);
2090*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(fGLSLExtendedInstructions, out);
2091*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(pick_by_type(type, floatInst, signedInst, unsignedInst, NA), out);
2092*c8dee2aaSAndroid Build Coastguard Worker     for (SpvId a : args) {
2093*c8dee2aaSAndroid Build Coastguard Worker         this->writeWord(a, out);
2094*c8dee2aaSAndroid Build Coastguard Worker     }
2095*c8dee2aaSAndroid Build Coastguard Worker }
2096*c8dee2aaSAndroid Build Coastguard Worker 
writeSpecialIntrinsic(const FunctionCall & c,SpecialIntrinsic kind,OutputStream & out)2097*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind,
2098*c8dee2aaSAndroid Build Coastguard Worker                                                 OutputStream& out) {
2099*c8dee2aaSAndroid Build Coastguard Worker     const ExpressionArray& arguments = c.arguments();
2100*c8dee2aaSAndroid Build Coastguard Worker     const Type& callType = c.type();
2101*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(nullptr);
2102*c8dee2aaSAndroid Build Coastguard Worker     switch (kind) {
2103*c8dee2aaSAndroid Build Coastguard Worker         case kAtan_SpecialIntrinsic: {
2104*c8dee2aaSAndroid Build Coastguard Worker             STArray<2, SpvId> argumentIds;
2105*c8dee2aaSAndroid Build Coastguard Worker             for (const std::unique_ptr<Expression>& arg : arguments) {
2106*c8dee2aaSAndroid Build Coastguard Worker                 argumentIds.push_back(this->writeExpression(*arg, out));
2107*c8dee2aaSAndroid Build Coastguard Worker             }
2108*c8dee2aaSAndroid Build Coastguard Worker             this->writeOpCode(SpvOpExtInst, 5 + (int32_t) argumentIds.size(), out);
2109*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(this->getType(callType), out);
2110*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(result, out);
2111*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(fGLSLExtendedInstructions, out);
2112*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(argumentIds.size() == 2 ? GLSLstd450Atan2 : GLSLstd450Atan, out);
2113*c8dee2aaSAndroid Build Coastguard Worker             for (SpvId id : argumentIds) {
2114*c8dee2aaSAndroid Build Coastguard Worker                 this->writeWord(id, out);
2115*c8dee2aaSAndroid Build Coastguard Worker             }
2116*c8dee2aaSAndroid Build Coastguard Worker             break;
2117*c8dee2aaSAndroid Build Coastguard Worker         }
2118*c8dee2aaSAndroid Build Coastguard Worker         case kSampledImage_SpecialIntrinsic: {
2119*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments.size() == 2);
2120*c8dee2aaSAndroid Build Coastguard Worker             SpvId img = this->writeExpression(*arguments[0], out);
2121*c8dee2aaSAndroid Build Coastguard Worker             SpvId sampler = this->writeExpression(*arguments[1], out);
2122*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpSampledImage,
2123*c8dee2aaSAndroid Build Coastguard Worker                                    this->getType(callType),
2124*c8dee2aaSAndroid Build Coastguard Worker                                    result,
2125*c8dee2aaSAndroid Build Coastguard Worker                                    img,
2126*c8dee2aaSAndroid Build Coastguard Worker                                    sampler,
2127*c8dee2aaSAndroid Build Coastguard Worker                                    out);
2128*c8dee2aaSAndroid Build Coastguard Worker             break;
2129*c8dee2aaSAndroid Build Coastguard Worker         }
2130*c8dee2aaSAndroid Build Coastguard Worker         case kSubpassLoad_SpecialIntrinsic: {
2131*c8dee2aaSAndroid Build Coastguard Worker             SpvId img = this->writeExpression(*arguments[0], out);
2132*c8dee2aaSAndroid Build Coastguard Worker             ExpressionArray args;
2133*c8dee2aaSAndroid Build Coastguard Worker             args.reserve_exact(2);
2134*c8dee2aaSAndroid Build Coastguard Worker             args.push_back(Literal::MakeInt(fContext, Position(), /*value=*/0));
2135*c8dee2aaSAndroid Build Coastguard Worker             args.push_back(Literal::MakeInt(fContext, Position(), /*value=*/0));
2136*c8dee2aaSAndroid Build Coastguard Worker             ConstructorCompound ctor(Position(), *fContext.fTypes.fInt2, std::move(args));
2137*c8dee2aaSAndroid Build Coastguard Worker             SpvId coords = this->writeExpression(ctor, out);
2138*c8dee2aaSAndroid Build Coastguard Worker             if (arguments.size() == 1) {
2139*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(SpvOpImageRead,
2140*c8dee2aaSAndroid Build Coastguard Worker                                        this->getType(callType),
2141*c8dee2aaSAndroid Build Coastguard Worker                                        result,
2142*c8dee2aaSAndroid Build Coastguard Worker                                        img,
2143*c8dee2aaSAndroid Build Coastguard Worker                                        coords,
2144*c8dee2aaSAndroid Build Coastguard Worker                                        out);
2145*c8dee2aaSAndroid Build Coastguard Worker             } else {
2146*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(arguments.size() == 2);
2147*c8dee2aaSAndroid Build Coastguard Worker                 SpvId sample = this->writeExpression(*arguments[1], out);
2148*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(SpvOpImageRead,
2149*c8dee2aaSAndroid Build Coastguard Worker                                        this->getType(callType),
2150*c8dee2aaSAndroid Build Coastguard Worker                                        result,
2151*c8dee2aaSAndroid Build Coastguard Worker                                        img,
2152*c8dee2aaSAndroid Build Coastguard Worker                                        coords,
2153*c8dee2aaSAndroid Build Coastguard Worker                                        SpvImageOperandsSampleMask,
2154*c8dee2aaSAndroid Build Coastguard Worker                                        sample,
2155*c8dee2aaSAndroid Build Coastguard Worker                                        out);
2156*c8dee2aaSAndroid Build Coastguard Worker             }
2157*c8dee2aaSAndroid Build Coastguard Worker             break;
2158*c8dee2aaSAndroid Build Coastguard Worker         }
2159*c8dee2aaSAndroid Build Coastguard Worker         case kTexture_SpecialIntrinsic: {
2160*c8dee2aaSAndroid Build Coastguard Worker             SpvOp_ op = SpvOpImageSampleImplicitLod;
2161*c8dee2aaSAndroid Build Coastguard Worker             const Type& arg1Type = arguments[1]->type();
2162*c8dee2aaSAndroid Build Coastguard Worker             switch (arguments[0]->type().dimensions()) {
2163*c8dee2aaSAndroid Build Coastguard Worker                 case SpvDim1D:
2164*c8dee2aaSAndroid Build Coastguard Worker                     if (arg1Type.matches(*fContext.fTypes.fFloat2)) {
2165*c8dee2aaSAndroid Build Coastguard Worker                         op = SpvOpImageSampleProjImplicitLod;
2166*c8dee2aaSAndroid Build Coastguard Worker                     } else {
2167*c8dee2aaSAndroid Build Coastguard Worker                         SkASSERT(arg1Type.matches(*fContext.fTypes.fFloat));
2168*c8dee2aaSAndroid Build Coastguard Worker                     }
2169*c8dee2aaSAndroid Build Coastguard Worker                     break;
2170*c8dee2aaSAndroid Build Coastguard Worker                 case SpvDim2D:
2171*c8dee2aaSAndroid Build Coastguard Worker                     if (arg1Type.matches(*fContext.fTypes.fFloat3)) {
2172*c8dee2aaSAndroid Build Coastguard Worker                         op = SpvOpImageSampleProjImplicitLod;
2173*c8dee2aaSAndroid Build Coastguard Worker                     } else {
2174*c8dee2aaSAndroid Build Coastguard Worker                         SkASSERT(arg1Type.matches(*fContext.fTypes.fFloat2));
2175*c8dee2aaSAndroid Build Coastguard Worker                     }
2176*c8dee2aaSAndroid Build Coastguard Worker                     break;
2177*c8dee2aaSAndroid Build Coastguard Worker                 case SpvDim3D:
2178*c8dee2aaSAndroid Build Coastguard Worker                     if (arg1Type.matches(*fContext.fTypes.fFloat4)) {
2179*c8dee2aaSAndroid Build Coastguard Worker                         op = SpvOpImageSampleProjImplicitLod;
2180*c8dee2aaSAndroid Build Coastguard Worker                     } else {
2181*c8dee2aaSAndroid Build Coastguard Worker                         SkASSERT(arg1Type.matches(*fContext.fTypes.fFloat3));
2182*c8dee2aaSAndroid Build Coastguard Worker                     }
2183*c8dee2aaSAndroid Build Coastguard Worker                     break;
2184*c8dee2aaSAndroid Build Coastguard Worker                 case SpvDimCube:   // fall through
2185*c8dee2aaSAndroid Build Coastguard Worker                 case SpvDimRect:   // fall through
2186*c8dee2aaSAndroid Build Coastguard Worker                 case SpvDimBuffer: // fall through
2187*c8dee2aaSAndroid Build Coastguard Worker                 case SpvDimSubpassData:
2188*c8dee2aaSAndroid Build Coastguard Worker                     break;
2189*c8dee2aaSAndroid Build Coastguard Worker             }
2190*c8dee2aaSAndroid Build Coastguard Worker             SpvId type = this->getType(callType);
2191*c8dee2aaSAndroid Build Coastguard Worker             SpvId sampler = this->writeExpression(*arguments[0], out);
2192*c8dee2aaSAndroid Build Coastguard Worker             SpvId uv = this->writeExpression(*arguments[1], out);
2193*c8dee2aaSAndroid Build Coastguard Worker             if (arguments.size() == 3) {
2194*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(op, type, result, sampler, uv,
2195*c8dee2aaSAndroid Build Coastguard Worker                                        SpvImageOperandsBiasMask,
2196*c8dee2aaSAndroid Build Coastguard Worker                                        this->writeExpression(*arguments[2], out),
2197*c8dee2aaSAndroid Build Coastguard Worker                                        out);
2198*c8dee2aaSAndroid Build Coastguard Worker             } else {
2199*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(arguments.size() == 2);
2200*c8dee2aaSAndroid Build Coastguard Worker                 if (fProgram.fConfig->fSettings.fSharpenTextures) {
2201*c8dee2aaSAndroid Build Coastguard Worker                     SpvId lodBias = this->writeLiteral(kSharpenTexturesBias,
2202*c8dee2aaSAndroid Build Coastguard Worker                                                        *fContext.fTypes.fFloat);
2203*c8dee2aaSAndroid Build Coastguard Worker                     this->writeInstruction(op, type, result, sampler, uv,
2204*c8dee2aaSAndroid Build Coastguard Worker                                            SpvImageOperandsBiasMask, lodBias, out);
2205*c8dee2aaSAndroid Build Coastguard Worker                 } else {
2206*c8dee2aaSAndroid Build Coastguard Worker                     this->writeInstruction(op, type, result, sampler, uv,
2207*c8dee2aaSAndroid Build Coastguard Worker                                            out);
2208*c8dee2aaSAndroid Build Coastguard Worker                 }
2209*c8dee2aaSAndroid Build Coastguard Worker             }
2210*c8dee2aaSAndroid Build Coastguard Worker             break;
2211*c8dee2aaSAndroid Build Coastguard Worker         }
2212*c8dee2aaSAndroid Build Coastguard Worker         case kTextureGrad_SpecialIntrinsic: {
2213*c8dee2aaSAndroid Build Coastguard Worker             SpvOp_ op = SpvOpImageSampleExplicitLod;
2214*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments.size() == 4);
2215*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[0]->type().dimensions() == SpvDim2D);
2216*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[1]->type().matches(*fContext.fTypes.fFloat2));
2217*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[2]->type().matches(*fContext.fTypes.fFloat2));
2218*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[3]->type().matches(*fContext.fTypes.fFloat2));
2219*c8dee2aaSAndroid Build Coastguard Worker             SpvId type = this->getType(callType);
2220*c8dee2aaSAndroid Build Coastguard Worker             SpvId sampler = this->writeExpression(*arguments[0], out);
2221*c8dee2aaSAndroid Build Coastguard Worker             SpvId uv = this->writeExpression(*arguments[1], out);
2222*c8dee2aaSAndroid Build Coastguard Worker             SpvId dPdx = this->writeExpression(*arguments[2], out);
2223*c8dee2aaSAndroid Build Coastguard Worker             SpvId dPdy = this->writeExpression(*arguments[3], out);
2224*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(op, type, result, sampler, uv, SpvImageOperandsGradMask,
2225*c8dee2aaSAndroid Build Coastguard Worker                                    dPdx, dPdy, out);
2226*c8dee2aaSAndroid Build Coastguard Worker             break;
2227*c8dee2aaSAndroid Build Coastguard Worker         }
2228*c8dee2aaSAndroid Build Coastguard Worker         case kTextureLod_SpecialIntrinsic: {
2229*c8dee2aaSAndroid Build Coastguard Worker             SpvOp_ op = SpvOpImageSampleExplicitLod;
2230*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments.size() == 3);
2231*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[0]->type().dimensions() == SpvDim2D);
2232*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[2]->type().matches(*fContext.fTypes.fFloat));
2233*c8dee2aaSAndroid Build Coastguard Worker             const Type& arg1Type = arguments[1]->type();
2234*c8dee2aaSAndroid Build Coastguard Worker             if (arg1Type.matches(*fContext.fTypes.fFloat3)) {
2235*c8dee2aaSAndroid Build Coastguard Worker                 op = SpvOpImageSampleProjExplicitLod;
2236*c8dee2aaSAndroid Build Coastguard Worker             } else {
2237*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(arg1Type.matches(*fContext.fTypes.fFloat2));
2238*c8dee2aaSAndroid Build Coastguard Worker             }
2239*c8dee2aaSAndroid Build Coastguard Worker             SpvId type = this->getType(callType);
2240*c8dee2aaSAndroid Build Coastguard Worker             SpvId sampler = this->writeExpression(*arguments[0], out);
2241*c8dee2aaSAndroid Build Coastguard Worker             SpvId uv = this->writeExpression(*arguments[1], out);
2242*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(op, type, result, sampler, uv,
2243*c8dee2aaSAndroid Build Coastguard Worker                                    SpvImageOperandsLodMask,
2244*c8dee2aaSAndroid Build Coastguard Worker                                    this->writeExpression(*arguments[2], out),
2245*c8dee2aaSAndroid Build Coastguard Worker                                    out);
2246*c8dee2aaSAndroid Build Coastguard Worker             break;
2247*c8dee2aaSAndroid Build Coastguard Worker         }
2248*c8dee2aaSAndroid Build Coastguard Worker         case kTextureRead_SpecialIntrinsic: {
2249*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[0]->type().dimensions() == SpvDim2D);
2250*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[1]->type().matches(*fContext.fTypes.fUInt2));
2251*c8dee2aaSAndroid Build Coastguard Worker 
2252*c8dee2aaSAndroid Build Coastguard Worker             SpvId type = this->getType(callType);
2253*c8dee2aaSAndroid Build Coastguard Worker             SpvId image = this->writeExpression(*arguments[0], out);
2254*c8dee2aaSAndroid Build Coastguard Worker             SpvId coord = this->writeExpression(*arguments[1], out);
2255*c8dee2aaSAndroid Build Coastguard Worker 
2256*c8dee2aaSAndroid Build Coastguard Worker             const Type& arg0Type = arguments[0]->type();
2257*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arg0Type.typeKind() == Type::TypeKind::kTexture);
2258*c8dee2aaSAndroid Build Coastguard Worker 
2259*c8dee2aaSAndroid Build Coastguard Worker             switch (arg0Type.textureAccess()) {
2260*c8dee2aaSAndroid Build Coastguard Worker                 case Type::TextureAccess::kSample:
2261*c8dee2aaSAndroid Build Coastguard Worker                     this->writeInstruction(SpvOpImageFetch, type, result, image, coord,
2262*c8dee2aaSAndroid Build Coastguard Worker                                            SpvImageOperandsLodMask,
2263*c8dee2aaSAndroid Build Coastguard Worker                                            this->writeOpConstant(*fContext.fTypes.fInt, 0),
2264*c8dee2aaSAndroid Build Coastguard Worker                                            out);
2265*c8dee2aaSAndroid Build Coastguard Worker                     break;
2266*c8dee2aaSAndroid Build Coastguard Worker                 case Type::TextureAccess::kRead:
2267*c8dee2aaSAndroid Build Coastguard Worker                 case Type::TextureAccess::kReadWrite:
2268*c8dee2aaSAndroid Build Coastguard Worker                     this->writeInstruction(SpvOpImageRead, type, result, image, coord, out);
2269*c8dee2aaSAndroid Build Coastguard Worker                     break;
2270*c8dee2aaSAndroid Build Coastguard Worker                 case Type::TextureAccess::kWrite:
2271*c8dee2aaSAndroid Build Coastguard Worker                 default:
2272*c8dee2aaSAndroid Build Coastguard Worker                     SkDEBUGFAIL("'textureRead' called on writeonly texture type");
2273*c8dee2aaSAndroid Build Coastguard Worker                     break;
2274*c8dee2aaSAndroid Build Coastguard Worker             }
2275*c8dee2aaSAndroid Build Coastguard Worker 
2276*c8dee2aaSAndroid Build Coastguard Worker             break;
2277*c8dee2aaSAndroid Build Coastguard Worker         }
2278*c8dee2aaSAndroid Build Coastguard Worker         case kTextureWrite_SpecialIntrinsic: {
2279*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[0]->type().dimensions() == SpvDim2D);
2280*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[1]->type().matches(*fContext.fTypes.fUInt2));
2281*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[2]->type().matches(*fContext.fTypes.fHalf4));
2282*c8dee2aaSAndroid Build Coastguard Worker 
2283*c8dee2aaSAndroid Build Coastguard Worker             SpvId image = this->writeExpression(*arguments[0], out);
2284*c8dee2aaSAndroid Build Coastguard Worker             SpvId coord = this->writeExpression(*arguments[1], out);
2285*c8dee2aaSAndroid Build Coastguard Worker             SpvId texel = this->writeExpression(*arguments[2], out);
2286*c8dee2aaSAndroid Build Coastguard Worker 
2287*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpImageWrite, image, coord, texel, out);
2288*c8dee2aaSAndroid Build Coastguard Worker             break;
2289*c8dee2aaSAndroid Build Coastguard Worker         }
2290*c8dee2aaSAndroid Build Coastguard Worker         case kTextureWidth_SpecialIntrinsic:
2291*c8dee2aaSAndroid Build Coastguard Worker         case kTextureHeight_SpecialIntrinsic: {
2292*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments[0]->type().dimensions() == SpvDim2D);
2293*c8dee2aaSAndroid Build Coastguard Worker             fCapabilities |= 1ULL << SpvCapabilityImageQuery;
2294*c8dee2aaSAndroid Build Coastguard Worker 
2295*c8dee2aaSAndroid Build Coastguard Worker             SpvId dimsType = this->getType(*fContext.fTypes.fUInt2);
2296*c8dee2aaSAndroid Build Coastguard Worker             SpvId dims = this->nextId(nullptr);
2297*c8dee2aaSAndroid Build Coastguard Worker             SpvId image = this->writeExpression(*arguments[0], out);
2298*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpImageQuerySize, dimsType, dims, image, out);
2299*c8dee2aaSAndroid Build Coastguard Worker 
2300*c8dee2aaSAndroid Build Coastguard Worker             SpvId type = this->getType(callType);
2301*c8dee2aaSAndroid Build Coastguard Worker             int32_t index = (kind == kTextureWidth_SpecialIntrinsic) ? 0 : 1;
2302*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpCompositeExtract, type, result, dims, index, out);
2303*c8dee2aaSAndroid Build Coastguard Worker             break;
2304*c8dee2aaSAndroid Build Coastguard Worker         }
2305*c8dee2aaSAndroid Build Coastguard Worker         case kMod_SpecialIntrinsic: {
2306*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> args = this->vectorize(arguments, out);
2307*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(args.size() == 2);
2308*c8dee2aaSAndroid Build Coastguard Worker             const Type& operandType = arguments[0]->type();
2309*c8dee2aaSAndroid Build Coastguard Worker             SpvOp_ op = pick_by_type(operandType, SpvOpFMod, SpvOpSMod, SpvOpUMod, SpvOpUndef);
2310*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(op != SpvOpUndef);
2311*c8dee2aaSAndroid Build Coastguard Worker             this->writeOpCode(op, 5, out);
2312*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(this->getType(operandType), out);
2313*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(result, out);
2314*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(args[0], out);
2315*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(args[1], out);
2316*c8dee2aaSAndroid Build Coastguard Worker             break;
2317*c8dee2aaSAndroid Build Coastguard Worker         }
2318*c8dee2aaSAndroid Build Coastguard Worker         case kDFdy_SpecialIntrinsic: {
2319*c8dee2aaSAndroid Build Coastguard Worker             SpvId fn = this->writeExpression(*arguments[0], out);
2320*c8dee2aaSAndroid Build Coastguard Worker             this->writeOpCode(SpvOpDPdy, 4, out);
2321*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(this->getType(callType), out);
2322*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(result, out);
2323*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(fn, out);
2324*c8dee2aaSAndroid Build Coastguard Worker             if (!fProgram.fConfig->fSettings.fForceNoRTFlip) {
2325*c8dee2aaSAndroid Build Coastguard Worker                 this->addRTFlipUniform(c.fPosition);
2326*c8dee2aaSAndroid Build Coastguard Worker                 ComponentArray componentArray;
2327*c8dee2aaSAndroid Build Coastguard Worker                 for (int index = 0; index < callType.columns(); ++index) {
2328*c8dee2aaSAndroid Build Coastguard Worker                     componentArray.push_back(SwizzleComponent::Y);
2329*c8dee2aaSAndroid Build Coastguard Worker                 }
2330*c8dee2aaSAndroid Build Coastguard Worker                 SpvId rtFlipY = this->writeSwizzle(*this->identifier(SKSL_RTFLIP_NAME),
2331*c8dee2aaSAndroid Build Coastguard Worker                                                    componentArray, out);
2332*c8dee2aaSAndroid Build Coastguard Worker                 SpvId flipped = this->nextId(&callType);
2333*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(SpvOpFMul, this->getType(callType), flipped, result,
2334*c8dee2aaSAndroid Build Coastguard Worker                                        rtFlipY, out);
2335*c8dee2aaSAndroid Build Coastguard Worker                 result = flipped;
2336*c8dee2aaSAndroid Build Coastguard Worker             }
2337*c8dee2aaSAndroid Build Coastguard Worker             break;
2338*c8dee2aaSAndroid Build Coastguard Worker         }
2339*c8dee2aaSAndroid Build Coastguard Worker         case kClamp_SpecialIntrinsic: {
2340*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> args = this->vectorize(arguments, out);
2341*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(args.size() == 3);
2342*c8dee2aaSAndroid Build Coastguard Worker             this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FClamp, GLSLstd450SClamp,
2343*c8dee2aaSAndroid Build Coastguard Worker                                                GLSLstd450UClamp, args, out);
2344*c8dee2aaSAndroid Build Coastguard Worker             break;
2345*c8dee2aaSAndroid Build Coastguard Worker         }
2346*c8dee2aaSAndroid Build Coastguard Worker         case kMax_SpecialIntrinsic: {
2347*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> args = this->vectorize(arguments, out);
2348*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(args.size() == 2);
2349*c8dee2aaSAndroid Build Coastguard Worker             this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FMax, GLSLstd450SMax,
2350*c8dee2aaSAndroid Build Coastguard Worker                                                GLSLstd450UMax, args, out);
2351*c8dee2aaSAndroid Build Coastguard Worker             break;
2352*c8dee2aaSAndroid Build Coastguard Worker         }
2353*c8dee2aaSAndroid Build Coastguard Worker         case kMin_SpecialIntrinsic: {
2354*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> args = this->vectorize(arguments, out);
2355*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(args.size() == 2);
2356*c8dee2aaSAndroid Build Coastguard Worker             this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FMin, GLSLstd450SMin,
2357*c8dee2aaSAndroid Build Coastguard Worker                                                GLSLstd450UMin, args, out);
2358*c8dee2aaSAndroid Build Coastguard Worker             break;
2359*c8dee2aaSAndroid Build Coastguard Worker         }
2360*c8dee2aaSAndroid Build Coastguard Worker         case kMix_SpecialIntrinsic: {
2361*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> args = this->vectorize(arguments, out);
2362*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(args.size() == 3);
2363*c8dee2aaSAndroid Build Coastguard Worker             if (arguments[2]->type().componentType().isBoolean()) {
2364*c8dee2aaSAndroid Build Coastguard Worker                 // Use OpSelect to implement Boolean mix().
2365*c8dee2aaSAndroid Build Coastguard Worker                 SpvId falseId     = this->writeExpression(*arguments[0], out);
2366*c8dee2aaSAndroid Build Coastguard Worker                 SpvId trueId      = this->writeExpression(*arguments[1], out);
2367*c8dee2aaSAndroid Build Coastguard Worker                 SpvId conditionId = this->writeExpression(*arguments[2], out);
2368*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(SpvOpSelect, this->getType(arguments[0]->type()), result,
2369*c8dee2aaSAndroid Build Coastguard Worker                                        conditionId, trueId, falseId, out);
2370*c8dee2aaSAndroid Build Coastguard Worker             } else {
2371*c8dee2aaSAndroid Build Coastguard Worker                 this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FMix, SpvOpUndef,
2372*c8dee2aaSAndroid Build Coastguard Worker                                                    SpvOpUndef, args, out);
2373*c8dee2aaSAndroid Build Coastguard Worker             }
2374*c8dee2aaSAndroid Build Coastguard Worker             break;
2375*c8dee2aaSAndroid Build Coastguard Worker         }
2376*c8dee2aaSAndroid Build Coastguard Worker         case kSaturate_SpecialIntrinsic: {
2377*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments.size() == 1);
2378*c8dee2aaSAndroid Build Coastguard Worker             int width = arguments[0]->type().columns();
2379*c8dee2aaSAndroid Build Coastguard Worker             STArray<3, SpvId> spvArgs{
2380*c8dee2aaSAndroid Build Coastguard Worker                 this->vectorize(*arguments[0], width, out),
2381*c8dee2aaSAndroid Build Coastguard Worker                 this->vectorize(*Literal::MakeFloat(fContext, Position(), /*value=*/0), width, out),
2382*c8dee2aaSAndroid Build Coastguard Worker                 this->vectorize(*Literal::MakeFloat(fContext, Position(), /*value=*/1), width, out),
2383*c8dee2aaSAndroid Build Coastguard Worker             };
2384*c8dee2aaSAndroid Build Coastguard Worker             this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FClamp, GLSLstd450SClamp,
2385*c8dee2aaSAndroid Build Coastguard Worker                                                GLSLstd450UClamp, spvArgs, out);
2386*c8dee2aaSAndroid Build Coastguard Worker             break;
2387*c8dee2aaSAndroid Build Coastguard Worker         }
2388*c8dee2aaSAndroid Build Coastguard Worker         case kSmoothStep_SpecialIntrinsic: {
2389*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> args = this->vectorize(arguments, out);
2390*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(args.size() == 3);
2391*c8dee2aaSAndroid Build Coastguard Worker             this->writeGLSLExtendedInstruction(callType, result, GLSLstd450SmoothStep, SpvOpUndef,
2392*c8dee2aaSAndroid Build Coastguard Worker                                                SpvOpUndef, args, out);
2393*c8dee2aaSAndroid Build Coastguard Worker             break;
2394*c8dee2aaSAndroid Build Coastguard Worker         }
2395*c8dee2aaSAndroid Build Coastguard Worker         case kStep_SpecialIntrinsic: {
2396*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> args = this->vectorize(arguments, out);
2397*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(args.size() == 2);
2398*c8dee2aaSAndroid Build Coastguard Worker             this->writeGLSLExtendedInstruction(callType, result, GLSLstd450Step, SpvOpUndef,
2399*c8dee2aaSAndroid Build Coastguard Worker                                                SpvOpUndef, args, out);
2400*c8dee2aaSAndroid Build Coastguard Worker             break;
2401*c8dee2aaSAndroid Build Coastguard Worker         }
2402*c8dee2aaSAndroid Build Coastguard Worker         case kMatrixCompMult_SpecialIntrinsic: {
2403*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments.size() == 2);
2404*c8dee2aaSAndroid Build Coastguard Worker             SpvId lhs = this->writeExpression(*arguments[0], out);
2405*c8dee2aaSAndroid Build Coastguard Worker             SpvId rhs = this->writeExpression(*arguments[1], out);
2406*c8dee2aaSAndroid Build Coastguard Worker             result = this->writeComponentwiseMatrixBinary(callType, lhs, rhs, SpvOpFMul, out);
2407*c8dee2aaSAndroid Build Coastguard Worker             break;
2408*c8dee2aaSAndroid Build Coastguard Worker         }
2409*c8dee2aaSAndroid Build Coastguard Worker         case kAtomicAdd_SpecialIntrinsic:
2410*c8dee2aaSAndroid Build Coastguard Worker         case kAtomicLoad_SpecialIntrinsic:
2411*c8dee2aaSAndroid Build Coastguard Worker         case kAtomicStore_SpecialIntrinsic:
2412*c8dee2aaSAndroid Build Coastguard Worker             result = this->writeAtomicIntrinsic(c, kind, result, out);
2413*c8dee2aaSAndroid Build Coastguard Worker             break;
2414*c8dee2aaSAndroid Build Coastguard Worker         case kStorageBarrier_SpecialIntrinsic:
2415*c8dee2aaSAndroid Build Coastguard Worker         case kWorkgroupBarrier_SpecialIntrinsic: {
2416*c8dee2aaSAndroid Build Coastguard Worker             // Both barrier types operate in the workgroup execution and memory scope and differ
2417*c8dee2aaSAndroid Build Coastguard Worker             // only in memory semantics. storageBarrier() is not a device-scope barrier.
2418*c8dee2aaSAndroid Build Coastguard Worker             SpvId scopeId =
2419*c8dee2aaSAndroid Build Coastguard Worker                     this->writeOpConstant(*fContext.fTypes.fUInt, (int32_t)SpvScopeWorkgroup);
2420*c8dee2aaSAndroid Build Coastguard Worker             int32_t memSemMask = (kind == kStorageBarrier_SpecialIntrinsic)
2421*c8dee2aaSAndroid Build Coastguard Worker                                          ? SpvMemorySemanticsAcquireReleaseMask |
2422*c8dee2aaSAndroid Build Coastguard Worker                                                    SpvMemorySemanticsUniformMemoryMask
2423*c8dee2aaSAndroid Build Coastguard Worker                                          : SpvMemorySemanticsAcquireReleaseMask |
2424*c8dee2aaSAndroid Build Coastguard Worker                                                    SpvMemorySemanticsWorkgroupMemoryMask;
2425*c8dee2aaSAndroid Build Coastguard Worker             SpvId memorySemanticsId = this->writeOpConstant(*fContext.fTypes.fUInt, memSemMask);
2426*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpControlBarrier,
2427*c8dee2aaSAndroid Build Coastguard Worker                                    scopeId,  // execution scope
2428*c8dee2aaSAndroid Build Coastguard Worker                                    scopeId,  // memory scope
2429*c8dee2aaSAndroid Build Coastguard Worker                                    memorySemanticsId,
2430*c8dee2aaSAndroid Build Coastguard Worker                                    out);
2431*c8dee2aaSAndroid Build Coastguard Worker             break;
2432*c8dee2aaSAndroid Build Coastguard Worker         }
2433*c8dee2aaSAndroid Build Coastguard Worker     }
2434*c8dee2aaSAndroid Build Coastguard Worker     return result;
2435*c8dee2aaSAndroid Build Coastguard Worker }
2436*c8dee2aaSAndroid Build Coastguard Worker 
writeAtomicIntrinsic(const FunctionCall & c,SpecialIntrinsic kind,SpvId resultId,OutputStream & out)2437*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeAtomicIntrinsic(const FunctionCall& c,
2438*c8dee2aaSAndroid Build Coastguard Worker                                                SpecialIntrinsic kind,
2439*c8dee2aaSAndroid Build Coastguard Worker                                                SpvId resultId,
2440*c8dee2aaSAndroid Build Coastguard Worker                                                OutputStream& out) {
2441*c8dee2aaSAndroid Build Coastguard Worker     const ExpressionArray& arguments = c.arguments();
2442*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!arguments.empty());
2443*c8dee2aaSAndroid Build Coastguard Worker 
2444*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> atomicPtr = this->getLValue(*arguments[0], out);
2445*c8dee2aaSAndroid Build Coastguard Worker     SpvId atomicPtrId = atomicPtr->getPointer();
2446*c8dee2aaSAndroid Build Coastguard Worker     if (atomicPtrId == NA) {
2447*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGFAILF("atomic intrinsic expected a pointer argument: %s",
2448*c8dee2aaSAndroid Build Coastguard Worker                      arguments[0]->description().c_str());
2449*c8dee2aaSAndroid Build Coastguard Worker         return NA;
2450*c8dee2aaSAndroid Build Coastguard Worker     }
2451*c8dee2aaSAndroid Build Coastguard Worker 
2452*c8dee2aaSAndroid Build Coastguard Worker     SpvId memoryScopeId = NA;
2453*c8dee2aaSAndroid Build Coastguard Worker     {
2454*c8dee2aaSAndroid Build Coastguard Worker         // In SkSL, the atomicUint type can only be declared as a workgroup variable or SSBO block
2455*c8dee2aaSAndroid Build Coastguard Worker         // member. The two memory scopes that these map to are "workgroup" and "device",
2456*c8dee2aaSAndroid Build Coastguard Worker         // respectively.
2457*c8dee2aaSAndroid Build Coastguard Worker         SpvScope memoryScope;
2458*c8dee2aaSAndroid Build Coastguard Worker         switch (atomicPtr->storageClass()) {
2459*c8dee2aaSAndroid Build Coastguard Worker             case StorageClass::kUniform:
2460*c8dee2aaSAndroid Build Coastguard Worker             case StorageClass::kStorageBuffer:
2461*c8dee2aaSAndroid Build Coastguard Worker                 // We encode storage buffers in the uniform address space (with the BufferBlock
2462*c8dee2aaSAndroid Build Coastguard Worker                 // decorator).
2463*c8dee2aaSAndroid Build Coastguard Worker                 memoryScope = SpvScopeDevice;
2464*c8dee2aaSAndroid Build Coastguard Worker                 break;
2465*c8dee2aaSAndroid Build Coastguard Worker             case StorageClass::kWorkgroup:
2466*c8dee2aaSAndroid Build Coastguard Worker                 memoryScope = SpvScopeWorkgroup;
2467*c8dee2aaSAndroid Build Coastguard Worker                 break;
2468*c8dee2aaSAndroid Build Coastguard Worker             default:
2469*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAILF("atomic argument has invalid storage class: %d",
2470*c8dee2aaSAndroid Build Coastguard Worker                              get_storage_class_spv_id(atomicPtr->storageClass()));
2471*c8dee2aaSAndroid Build Coastguard Worker                 return NA;
2472*c8dee2aaSAndroid Build Coastguard Worker         }
2473*c8dee2aaSAndroid Build Coastguard Worker         memoryScopeId = this->writeOpConstant(*fContext.fTypes.fUInt, (int32_t)memoryScope);
2474*c8dee2aaSAndroid Build Coastguard Worker     }
2475*c8dee2aaSAndroid Build Coastguard Worker 
2476*c8dee2aaSAndroid Build Coastguard Worker     SpvId relaxedMemoryOrderId =
2477*c8dee2aaSAndroid Build Coastguard Worker             this->writeOpConstant(*fContext.fTypes.fUInt, (int32_t)SpvMemorySemanticsMaskNone);
2478*c8dee2aaSAndroid Build Coastguard Worker 
2479*c8dee2aaSAndroid Build Coastguard Worker     switch (kind) {
2480*c8dee2aaSAndroid Build Coastguard Worker         case kAtomicAdd_SpecialIntrinsic:
2481*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments.size() == 2);
2482*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpAtomicIAdd,
2483*c8dee2aaSAndroid Build Coastguard Worker                                    this->getType(c.type()),
2484*c8dee2aaSAndroid Build Coastguard Worker                                    resultId,
2485*c8dee2aaSAndroid Build Coastguard Worker                                    atomicPtrId,
2486*c8dee2aaSAndroid Build Coastguard Worker                                    memoryScopeId,
2487*c8dee2aaSAndroid Build Coastguard Worker                                    relaxedMemoryOrderId,
2488*c8dee2aaSAndroid Build Coastguard Worker                                    this->writeExpression(*arguments[1], out),
2489*c8dee2aaSAndroid Build Coastguard Worker                                    out);
2490*c8dee2aaSAndroid Build Coastguard Worker             break;
2491*c8dee2aaSAndroid Build Coastguard Worker         case kAtomicLoad_SpecialIntrinsic:
2492*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments.size() == 1);
2493*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpAtomicLoad,
2494*c8dee2aaSAndroid Build Coastguard Worker                                    this->getType(c.type()),
2495*c8dee2aaSAndroid Build Coastguard Worker                                    resultId,
2496*c8dee2aaSAndroid Build Coastguard Worker                                    atomicPtrId,
2497*c8dee2aaSAndroid Build Coastguard Worker                                    memoryScopeId,
2498*c8dee2aaSAndroid Build Coastguard Worker                                    relaxedMemoryOrderId,
2499*c8dee2aaSAndroid Build Coastguard Worker                                    out);
2500*c8dee2aaSAndroid Build Coastguard Worker             break;
2501*c8dee2aaSAndroid Build Coastguard Worker         case kAtomicStore_SpecialIntrinsic:
2502*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(arguments.size() == 2);
2503*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpAtomicStore,
2504*c8dee2aaSAndroid Build Coastguard Worker                                    atomicPtrId,
2505*c8dee2aaSAndroid Build Coastguard Worker                                    memoryScopeId,
2506*c8dee2aaSAndroid Build Coastguard Worker                                    relaxedMemoryOrderId,
2507*c8dee2aaSAndroid Build Coastguard Worker                                    this->writeExpression(*arguments[1], out),
2508*c8dee2aaSAndroid Build Coastguard Worker                                    out);
2509*c8dee2aaSAndroid Build Coastguard Worker             break;
2510*c8dee2aaSAndroid Build Coastguard Worker         default:
2511*c8dee2aaSAndroid Build Coastguard Worker             SkUNREACHABLE;
2512*c8dee2aaSAndroid Build Coastguard Worker     }
2513*c8dee2aaSAndroid Build Coastguard Worker 
2514*c8dee2aaSAndroid Build Coastguard Worker     return resultId;
2515*c8dee2aaSAndroid Build Coastguard Worker }
2516*c8dee2aaSAndroid Build Coastguard Worker 
writeFunctionCallArgument(TArray<SpvId> & argumentList,const FunctionCall & call,int argIndex,std::vector<TempVar> * tempVars,const SkBitSet * specializedParams,OutputStream & out)2517*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeFunctionCallArgument(TArray<SpvId>& argumentList,
2518*c8dee2aaSAndroid Build Coastguard Worker                                                    const FunctionCall& call,
2519*c8dee2aaSAndroid Build Coastguard Worker                                                    int argIndex,
2520*c8dee2aaSAndroid Build Coastguard Worker                                                    std::vector<TempVar>* tempVars,
2521*c8dee2aaSAndroid Build Coastguard Worker                                                    const SkBitSet* specializedParams,
2522*c8dee2aaSAndroid Build Coastguard Worker                                                    OutputStream& out) {
2523*c8dee2aaSAndroid Build Coastguard Worker     const FunctionDeclaration& funcDecl = call.function();
2524*c8dee2aaSAndroid Build Coastguard Worker     const Expression& arg = *call.arguments()[argIndex];
2525*c8dee2aaSAndroid Build Coastguard Worker     const Variable* param = funcDecl.parameters()[argIndex];
2526*c8dee2aaSAndroid Build Coastguard Worker     bool paramIsSpecialized = specializedParams && specializedParams->test(argIndex);
2527*c8dee2aaSAndroid Build Coastguard Worker     ModifierFlags paramFlags = param->modifierFlags();
2528*c8dee2aaSAndroid Build Coastguard Worker 
2529*c8dee2aaSAndroid Build Coastguard Worker     // Ignore the argument since it is specialized, if fUseTextureSamplerPairs is true and this
2530*c8dee2aaSAndroid Build Coastguard Worker     // argument is a sampler, handle ignoring the sampler below when generating the texture and
2531*c8dee2aaSAndroid Build Coastguard Worker     // sampler pair arguments.
2532*c8dee2aaSAndroid Build Coastguard Worker     if (paramIsSpecialized && !(param->type().isSampler() && fUseTextureSamplerPairs)) {
2533*c8dee2aaSAndroid Build Coastguard Worker         return;
2534*c8dee2aaSAndroid Build Coastguard Worker     }
2535*c8dee2aaSAndroid Build Coastguard Worker 
2536*c8dee2aaSAndroid Build Coastguard Worker     if (arg.is<VariableReference>() && (arg.type().typeKind() == Type::TypeKind::kSampler ||
2537*c8dee2aaSAndroid Build Coastguard Worker                                         arg.type().typeKind() == Type::TypeKind::kSeparateSampler ||
2538*c8dee2aaSAndroid Build Coastguard Worker                                         arg.type().typeKind() == Type::TypeKind::kTexture)) {
2539*c8dee2aaSAndroid Build Coastguard Worker         // Opaque handle (sampler/texture) arguments are always declared as pointers but never
2540*c8dee2aaSAndroid Build Coastguard Worker         // stored in intermediates when calling user-defined functions.
2541*c8dee2aaSAndroid Build Coastguard Worker         //
2542*c8dee2aaSAndroid Build Coastguard Worker         // The case for intrinsics (which take opaque arguments by value) is handled above just like
2543*c8dee2aaSAndroid Build Coastguard Worker         // regular pointers.
2544*c8dee2aaSAndroid Build Coastguard Worker         //
2545*c8dee2aaSAndroid Build Coastguard Worker         // See getFunctionParameterType for further explanation.
2546*c8dee2aaSAndroid Build Coastguard Worker         const Variable* var = arg.as<VariableReference>().variable();
2547*c8dee2aaSAndroid Build Coastguard Worker 
2548*c8dee2aaSAndroid Build Coastguard Worker         // In Dawn-mode the texture and sampler arguments are forwarded to the helper function.
2549*c8dee2aaSAndroid Build Coastguard Worker         if (fUseTextureSamplerPairs && var->type().isSampler()) {
2550*c8dee2aaSAndroid Build Coastguard Worker             if (const auto* p = fSynthesizedSamplerMap.find(var)) {
2551*c8dee2aaSAndroid Build Coastguard Worker                 SpvId* img = fVariableMap.find((*p)->fTexture.get());
2552*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(img);
2553*c8dee2aaSAndroid Build Coastguard Worker 
2554*c8dee2aaSAndroid Build Coastguard Worker                 argumentList.push_back(*img);
2555*c8dee2aaSAndroid Build Coastguard Worker 
2556*c8dee2aaSAndroid Build Coastguard Worker                 if (!paramIsSpecialized) {
2557*c8dee2aaSAndroid Build Coastguard Worker                     SpvId* sampler = fVariableMap.find((*p)->fSampler.get());
2558*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(sampler);
2559*c8dee2aaSAndroid Build Coastguard Worker                     argumentList.push_back(*sampler);
2560*c8dee2aaSAndroid Build Coastguard Worker                 }
2561*c8dee2aaSAndroid Build Coastguard Worker                 return;
2562*c8dee2aaSAndroid Build Coastguard Worker             }
2563*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("sampler missing from fSynthesizedSamplerMap");
2564*c8dee2aaSAndroid Build Coastguard Worker         }
2565*c8dee2aaSAndroid Build Coastguard Worker 
2566*c8dee2aaSAndroid Build Coastguard Worker         SpvId* entry = fVariableMap.find(var);
2567*c8dee2aaSAndroid Build Coastguard Worker         SkASSERTF(entry, "%s", arg.description().c_str());
2568*c8dee2aaSAndroid Build Coastguard Worker         argumentList.push_back(*entry);
2569*c8dee2aaSAndroid Build Coastguard Worker         return;
2570*c8dee2aaSAndroid Build Coastguard Worker     }
2571*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!paramIsSpecialized);
2572*c8dee2aaSAndroid Build Coastguard Worker 
2573*c8dee2aaSAndroid Build Coastguard Worker     // ID of temporary variable that we will use to hold this argument, or 0 if it is being
2574*c8dee2aaSAndroid Build Coastguard Worker     // passed directly
2575*c8dee2aaSAndroid Build Coastguard Worker     SpvId tmpVar = NA;
2576*c8dee2aaSAndroid Build Coastguard Worker     // if we need a temporary var to store this argument, this is the value to store in the var
2577*c8dee2aaSAndroid Build Coastguard Worker     SpvId tmpValueId = NA;
2578*c8dee2aaSAndroid Build Coastguard Worker 
2579*c8dee2aaSAndroid Build Coastguard Worker     if (is_out(paramFlags)) {
2580*c8dee2aaSAndroid Build Coastguard Worker         std::unique_ptr<LValue> lv = this->getLValue(arg, out);
2581*c8dee2aaSAndroid Build Coastguard Worker         // We handle out params with a temp var that we copy back to the original variable at the
2582*c8dee2aaSAndroid Build Coastguard Worker         // end of the call. GLSL guarantees that the original variable will be unchanged until the
2583*c8dee2aaSAndroid Build Coastguard Worker         // end of the call, and also that out params are written back to their original variables in
2584*c8dee2aaSAndroid Build Coastguard Worker         // a specific order (left-to-right), so it's unsafe to pass a pointer to the original value.
2585*c8dee2aaSAndroid Build Coastguard Worker         if (is_in(paramFlags)) {
2586*c8dee2aaSAndroid Build Coastguard Worker             tmpValueId = lv->load(out);
2587*c8dee2aaSAndroid Build Coastguard Worker         }
2588*c8dee2aaSAndroid Build Coastguard Worker         tmpVar = this->nextId(&arg.type());
2589*c8dee2aaSAndroid Build Coastguard Worker         tempVars->push_back(TempVar{tmpVar, &arg.type(), std::move(lv)});
2590*c8dee2aaSAndroid Build Coastguard Worker     } else if (funcDecl.isIntrinsic()) {
2591*c8dee2aaSAndroid Build Coastguard Worker         // Unlike user function calls, non-out intrinsic arguments don't need pointer parameters.
2592*c8dee2aaSAndroid Build Coastguard Worker         argumentList.push_back(this->writeExpression(arg, out));
2593*c8dee2aaSAndroid Build Coastguard Worker         return;
2594*c8dee2aaSAndroid Build Coastguard Worker     } else {
2595*c8dee2aaSAndroid Build Coastguard Worker         // We always use pointer parameters when calling user functions.
2596*c8dee2aaSAndroid Build Coastguard Worker         // See getFunctionParameterType for further explanation.
2597*c8dee2aaSAndroid Build Coastguard Worker         tmpValueId = this->writeExpression(arg, out);
2598*c8dee2aaSAndroid Build Coastguard Worker         tmpVar = this->nextId(nullptr);
2599*c8dee2aaSAndroid Build Coastguard Worker     }
2600*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpVariable,
2601*c8dee2aaSAndroid Build Coastguard Worker                            this->getPointerType(arg.type(), StorageClass::kFunction),
2602*c8dee2aaSAndroid Build Coastguard Worker                            tmpVar,
2603*c8dee2aaSAndroid Build Coastguard Worker                            SpvStorageClassFunction,
2604*c8dee2aaSAndroid Build Coastguard Worker                            fVariableBuffer);
2605*c8dee2aaSAndroid Build Coastguard Worker     if (tmpValueId != NA) {
2606*c8dee2aaSAndroid Build Coastguard Worker         this->writeOpStore(StorageClass::kFunction, tmpVar, tmpValueId, out);
2607*c8dee2aaSAndroid Build Coastguard Worker     }
2608*c8dee2aaSAndroid Build Coastguard Worker     argumentList.push_back(tmpVar);
2609*c8dee2aaSAndroid Build Coastguard Worker }
2610*c8dee2aaSAndroid Build Coastguard Worker 
copyBackTempVars(const std::vector<TempVar> & tempVars,OutputStream & out)2611*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::copyBackTempVars(const std::vector<TempVar>& tempVars, OutputStream& out) {
2612*c8dee2aaSAndroid Build Coastguard Worker     for (const TempVar& tempVar : tempVars) {
2613*c8dee2aaSAndroid Build Coastguard Worker         SpvId load = this->nextId(tempVar.type);
2614*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpLoad, this->getType(*tempVar.type), load, tempVar.spvId, out);
2615*c8dee2aaSAndroid Build Coastguard Worker         tempVar.lvalue->store(load, out);
2616*c8dee2aaSAndroid Build Coastguard Worker     }
2617*c8dee2aaSAndroid Build Coastguard Worker }
2618*c8dee2aaSAndroid Build Coastguard Worker 
writeFunctionCall(const FunctionCall & c,OutputStream & out)2619*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeFunctionCall(const FunctionCall& c, OutputStream& out) {
2620*c8dee2aaSAndroid Build Coastguard Worker     // Handle intrinsics.
2621*c8dee2aaSAndroid Build Coastguard Worker     const FunctionDeclaration& function = c.function();
2622*c8dee2aaSAndroid Build Coastguard Worker     if (function.isIntrinsic() && !function.definition()) {
2623*c8dee2aaSAndroid Build Coastguard Worker         return this->writeIntrinsicCall(c, out);
2624*c8dee2aaSAndroid Build Coastguard Worker     }
2625*c8dee2aaSAndroid Build Coastguard Worker 
2626*c8dee2aaSAndroid Build Coastguard Worker     // Look up this function (or its specialization, if any) in our map of function SpvIds.
2627*c8dee2aaSAndroid Build Coastguard Worker     Analysis::SpecializationIndex specializationIndex = Analysis::FindSpecializationIndexForCall(
2628*c8dee2aaSAndroid Build Coastguard Worker             c, fSpecializationInfo, fActiveSpecializationIndex);
2629*c8dee2aaSAndroid Build Coastguard Worker     SpvId* entry = fFunctionMap.find({&function, specializationIndex});
2630*c8dee2aaSAndroid Build Coastguard Worker     if (!entry) {
2631*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(c.fPosition, "function '" + function.description() +
2632*c8dee2aaSAndroid Build Coastguard Worker                                              "' is not defined");
2633*c8dee2aaSAndroid Build Coastguard Worker         return NA;
2634*c8dee2aaSAndroid Build Coastguard Worker     }
2635*c8dee2aaSAndroid Build Coastguard Worker 
2636*c8dee2aaSAndroid Build Coastguard Worker     // If we are calling a specialized function, we need to gather the specialized parameters
2637*c8dee2aaSAndroid Build Coastguard Worker     // so we can remove them from the argument list.
2638*c8dee2aaSAndroid Build Coastguard Worker     SkBitSet specializedParams =
2639*c8dee2aaSAndroid Build Coastguard Worker             Analysis::FindSpecializedParametersForFunction(c.function(), fSpecializationInfo);
2640*c8dee2aaSAndroid Build Coastguard Worker 
2641*c8dee2aaSAndroid Build Coastguard Worker     // Temp variables are used to write back out-parameters after the function call is complete.
2642*c8dee2aaSAndroid Build Coastguard Worker     const ExpressionArray& arguments = c.arguments();
2643*c8dee2aaSAndroid Build Coastguard Worker     std::vector<TempVar> tempVars;
2644*c8dee2aaSAndroid Build Coastguard Worker     TArray<SpvId> argumentIds;
2645*c8dee2aaSAndroid Build Coastguard Worker     argumentIds.reserve_exact(arguments.size());
2646*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < arguments.size(); i++) {
2647*c8dee2aaSAndroid Build Coastguard Worker         this->writeFunctionCallArgument(argumentIds, c, i, &tempVars, &specializedParams, out);
2648*c8dee2aaSAndroid Build Coastguard Worker     }
2649*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(nullptr);
2650*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(SpvOpFunctionCall, 4 + (int32_t)argumentIds.size(), out);
2651*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(this->getType(c.type()), out);
2652*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(result, out);
2653*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(*entry, out);
2654*c8dee2aaSAndroid Build Coastguard Worker     for (SpvId id : argumentIds) {
2655*c8dee2aaSAndroid Build Coastguard Worker         this->writeWord(id, out);
2656*c8dee2aaSAndroid Build Coastguard Worker     }
2657*c8dee2aaSAndroid Build Coastguard Worker     // Now that the call is complete, we copy temp out-variables back to their real lvalues.
2658*c8dee2aaSAndroid Build Coastguard Worker     this->copyBackTempVars(tempVars, out);
2659*c8dee2aaSAndroid Build Coastguard Worker     return result;
2660*c8dee2aaSAndroid Build Coastguard Worker }
2661*c8dee2aaSAndroid Build Coastguard Worker 
castScalarToType(SpvId inputExprId,const Type & inputType,const Type & outputType,OutputStream & out)2662*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::castScalarToType(SpvId inputExprId,
2663*c8dee2aaSAndroid Build Coastguard Worker                                            const Type& inputType,
2664*c8dee2aaSAndroid Build Coastguard Worker                                            const Type& outputType,
2665*c8dee2aaSAndroid Build Coastguard Worker                                            OutputStream& out) {
2666*c8dee2aaSAndroid Build Coastguard Worker     if (outputType.isFloat()) {
2667*c8dee2aaSAndroid Build Coastguard Worker         return this->castScalarToFloat(inputExprId, inputType, outputType, out);
2668*c8dee2aaSAndroid Build Coastguard Worker     }
2669*c8dee2aaSAndroid Build Coastguard Worker     if (outputType.isSigned()) {
2670*c8dee2aaSAndroid Build Coastguard Worker         return this->castScalarToSignedInt(inputExprId, inputType, outputType, out);
2671*c8dee2aaSAndroid Build Coastguard Worker     }
2672*c8dee2aaSAndroid Build Coastguard Worker     if (outputType.isUnsigned()) {
2673*c8dee2aaSAndroid Build Coastguard Worker         return this->castScalarToUnsignedInt(inputExprId, inputType, outputType, out);
2674*c8dee2aaSAndroid Build Coastguard Worker     }
2675*c8dee2aaSAndroid Build Coastguard Worker     if (outputType.isBoolean()) {
2676*c8dee2aaSAndroid Build Coastguard Worker         return this->castScalarToBoolean(inputExprId, inputType, outputType, out);
2677*c8dee2aaSAndroid Build Coastguard Worker     }
2678*c8dee2aaSAndroid Build Coastguard Worker 
2679*c8dee2aaSAndroid Build Coastguard Worker     fContext.fErrors->error(Position(), "unsupported cast: " + inputType.description() + " to " +
2680*c8dee2aaSAndroid Build Coastguard Worker             outputType.description());
2681*c8dee2aaSAndroid Build Coastguard Worker     return inputExprId;
2682*c8dee2aaSAndroid Build Coastguard Worker }
2683*c8dee2aaSAndroid Build Coastguard Worker 
castScalarToFloat(SpvId inputId,const Type & inputType,const Type & outputType,OutputStream & out)2684*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::castScalarToFloat(SpvId inputId, const Type& inputType,
2685*c8dee2aaSAndroid Build Coastguard Worker                                             const Type& outputType, OutputStream& out) {
2686*c8dee2aaSAndroid Build Coastguard Worker     // Casting a float to float is a no-op.
2687*c8dee2aaSAndroid Build Coastguard Worker     if (inputType.isFloat()) {
2688*c8dee2aaSAndroid Build Coastguard Worker         return inputId;
2689*c8dee2aaSAndroid Build Coastguard Worker     }
2690*c8dee2aaSAndroid Build Coastguard Worker 
2691*c8dee2aaSAndroid Build Coastguard Worker     // Given the input type, generate the appropriate instruction to cast to float.
2692*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(&outputType);
2693*c8dee2aaSAndroid Build Coastguard Worker     if (inputType.isBoolean()) {
2694*c8dee2aaSAndroid Build Coastguard Worker         // Use OpSelect to convert the boolean argument to a literal 1.0 or 0.0.
2695*c8dee2aaSAndroid Build Coastguard Worker         const SpvId oneID = this->writeLiteral(1.0, *fContext.fTypes.fFloat);
2696*c8dee2aaSAndroid Build Coastguard Worker         const SpvId zeroID = this->writeLiteral(0.0, *fContext.fTypes.fFloat);
2697*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpSelect, this->getType(outputType), result,
2698*c8dee2aaSAndroid Build Coastguard Worker                                inputId, oneID, zeroID, out);
2699*c8dee2aaSAndroid Build Coastguard Worker     } else if (inputType.isSigned()) {
2700*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpConvertSToF, this->getType(outputType), result, inputId, out);
2701*c8dee2aaSAndroid Build Coastguard Worker     } else if (inputType.isUnsigned()) {
2702*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpConvertUToF, this->getType(outputType), result, inputId, out);
2703*c8dee2aaSAndroid Build Coastguard Worker     } else {
2704*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGFAILF("unsupported type for float typecast: %s", inputType.description().c_str());
2705*c8dee2aaSAndroid Build Coastguard Worker         return NA;
2706*c8dee2aaSAndroid Build Coastguard Worker     }
2707*c8dee2aaSAndroid Build Coastguard Worker     return result;
2708*c8dee2aaSAndroid Build Coastguard Worker }
2709*c8dee2aaSAndroid Build Coastguard Worker 
castScalarToSignedInt(SpvId inputId,const Type & inputType,const Type & outputType,OutputStream & out)2710*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::castScalarToSignedInt(SpvId inputId, const Type& inputType,
2711*c8dee2aaSAndroid Build Coastguard Worker                                                 const Type& outputType, OutputStream& out) {
2712*c8dee2aaSAndroid Build Coastguard Worker     // Casting a signed int to signed int is a no-op.
2713*c8dee2aaSAndroid Build Coastguard Worker     if (inputType.isSigned()) {
2714*c8dee2aaSAndroid Build Coastguard Worker         return inputId;
2715*c8dee2aaSAndroid Build Coastguard Worker     }
2716*c8dee2aaSAndroid Build Coastguard Worker 
2717*c8dee2aaSAndroid Build Coastguard Worker     // Given the input type, generate the appropriate instruction to cast to signed int.
2718*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(&outputType);
2719*c8dee2aaSAndroid Build Coastguard Worker     if (inputType.isBoolean()) {
2720*c8dee2aaSAndroid Build Coastguard Worker         // Use OpSelect to convert the boolean argument to a literal 1 or 0.
2721*c8dee2aaSAndroid Build Coastguard Worker         const SpvId oneID = this->writeLiteral(1.0, *fContext.fTypes.fInt);
2722*c8dee2aaSAndroid Build Coastguard Worker         const SpvId zeroID = this->writeLiteral(0.0, *fContext.fTypes.fInt);
2723*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpSelect, this->getType(outputType), result,
2724*c8dee2aaSAndroid Build Coastguard Worker                                inputId, oneID, zeroID, out);
2725*c8dee2aaSAndroid Build Coastguard Worker     } else if (inputType.isFloat()) {
2726*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpConvertFToS, this->getType(outputType), result, inputId, out);
2727*c8dee2aaSAndroid Build Coastguard Worker     } else if (inputType.isUnsigned()) {
2728*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpBitcast, this->getType(outputType), result, inputId, out);
2729*c8dee2aaSAndroid Build Coastguard Worker     } else {
2730*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGFAILF("unsupported type for signed int typecast: %s",
2731*c8dee2aaSAndroid Build Coastguard Worker                      inputType.description().c_str());
2732*c8dee2aaSAndroid Build Coastguard Worker         return NA;
2733*c8dee2aaSAndroid Build Coastguard Worker     }
2734*c8dee2aaSAndroid Build Coastguard Worker     return result;
2735*c8dee2aaSAndroid Build Coastguard Worker }
2736*c8dee2aaSAndroid Build Coastguard Worker 
castScalarToUnsignedInt(SpvId inputId,const Type & inputType,const Type & outputType,OutputStream & out)2737*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::castScalarToUnsignedInt(SpvId inputId, const Type& inputType,
2738*c8dee2aaSAndroid Build Coastguard Worker                                                   const Type& outputType, OutputStream& out) {
2739*c8dee2aaSAndroid Build Coastguard Worker     // Casting an unsigned int to unsigned int is a no-op.
2740*c8dee2aaSAndroid Build Coastguard Worker     if (inputType.isUnsigned()) {
2741*c8dee2aaSAndroid Build Coastguard Worker         return inputId;
2742*c8dee2aaSAndroid Build Coastguard Worker     }
2743*c8dee2aaSAndroid Build Coastguard Worker 
2744*c8dee2aaSAndroid Build Coastguard Worker     // Given the input type, generate the appropriate instruction to cast to unsigned int.
2745*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(&outputType);
2746*c8dee2aaSAndroid Build Coastguard Worker     if (inputType.isBoolean()) {
2747*c8dee2aaSAndroid Build Coastguard Worker         // Use OpSelect to convert the boolean argument to a literal 1u or 0u.
2748*c8dee2aaSAndroid Build Coastguard Worker         const SpvId oneID = this->writeLiteral(1.0, *fContext.fTypes.fUInt);
2749*c8dee2aaSAndroid Build Coastguard Worker         const SpvId zeroID = this->writeLiteral(0.0, *fContext.fTypes.fUInt);
2750*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpSelect, this->getType(outputType), result,
2751*c8dee2aaSAndroid Build Coastguard Worker                                inputId, oneID, zeroID, out);
2752*c8dee2aaSAndroid Build Coastguard Worker     } else if (inputType.isFloat()) {
2753*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpConvertFToU, this->getType(outputType), result, inputId, out);
2754*c8dee2aaSAndroid Build Coastguard Worker     } else if (inputType.isSigned()) {
2755*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpBitcast, this->getType(outputType), result, inputId, out);
2756*c8dee2aaSAndroid Build Coastguard Worker     } else {
2757*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGFAILF("unsupported type for unsigned int typecast: %s",
2758*c8dee2aaSAndroid Build Coastguard Worker                      inputType.description().c_str());
2759*c8dee2aaSAndroid Build Coastguard Worker         return NA;
2760*c8dee2aaSAndroid Build Coastguard Worker     }
2761*c8dee2aaSAndroid Build Coastguard Worker     return result;
2762*c8dee2aaSAndroid Build Coastguard Worker }
2763*c8dee2aaSAndroid Build Coastguard Worker 
castScalarToBoolean(SpvId inputId,const Type & inputType,const Type & outputType,OutputStream & out)2764*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::castScalarToBoolean(SpvId inputId, const Type& inputType,
2765*c8dee2aaSAndroid Build Coastguard Worker                                               const Type& outputType, OutputStream& out) {
2766*c8dee2aaSAndroid Build Coastguard Worker     // Casting a bool to bool is a no-op.
2767*c8dee2aaSAndroid Build Coastguard Worker     if (inputType.isBoolean()) {
2768*c8dee2aaSAndroid Build Coastguard Worker         return inputId;
2769*c8dee2aaSAndroid Build Coastguard Worker     }
2770*c8dee2aaSAndroid Build Coastguard Worker 
2771*c8dee2aaSAndroid Build Coastguard Worker     // Given the input type, generate the appropriate instruction to cast to bool.
2772*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(nullptr);
2773*c8dee2aaSAndroid Build Coastguard Worker     if (inputType.isSigned()) {
2774*c8dee2aaSAndroid Build Coastguard Worker         // Synthesize a boolean result by comparing the input against a signed zero literal.
2775*c8dee2aaSAndroid Build Coastguard Worker         const SpvId zeroID = this->writeLiteral(0.0, *fContext.fTypes.fInt);
2776*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpINotEqual, this->getType(outputType), result,
2777*c8dee2aaSAndroid Build Coastguard Worker                                inputId, zeroID, out);
2778*c8dee2aaSAndroid Build Coastguard Worker     } else if (inputType.isUnsigned()) {
2779*c8dee2aaSAndroid Build Coastguard Worker         // Synthesize a boolean result by comparing the input against an unsigned zero literal.
2780*c8dee2aaSAndroid Build Coastguard Worker         const SpvId zeroID = this->writeLiteral(0.0, *fContext.fTypes.fUInt);
2781*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpINotEqual, this->getType(outputType), result,
2782*c8dee2aaSAndroid Build Coastguard Worker                                inputId, zeroID, out);
2783*c8dee2aaSAndroid Build Coastguard Worker     } else if (inputType.isFloat()) {
2784*c8dee2aaSAndroid Build Coastguard Worker         // Synthesize a boolean result by comparing the input against a floating-point zero literal.
2785*c8dee2aaSAndroid Build Coastguard Worker         const SpvId zeroID = this->writeLiteral(0.0, *fContext.fTypes.fFloat);
2786*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpFUnordNotEqual, this->getType(outputType), result,
2787*c8dee2aaSAndroid Build Coastguard Worker                                inputId, zeroID, out);
2788*c8dee2aaSAndroid Build Coastguard Worker     } else {
2789*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGFAILF("unsupported type for boolean typecast: %s", inputType.description().c_str());
2790*c8dee2aaSAndroid Build Coastguard Worker         return NA;
2791*c8dee2aaSAndroid Build Coastguard Worker     }
2792*c8dee2aaSAndroid Build Coastguard Worker     return result;
2793*c8dee2aaSAndroid Build Coastguard Worker }
2794*c8dee2aaSAndroid Build Coastguard Worker 
writeMatrixCopy(SpvId src,const Type & srcType,const Type & dstType,OutputStream & out)2795*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeMatrixCopy(SpvId src, const Type& srcType, const Type& dstType,
2796*c8dee2aaSAndroid Build Coastguard Worker                                           OutputStream& out) {
2797*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(srcType.isMatrix());
2798*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(dstType.isMatrix());
2799*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(srcType.componentType().matches(dstType.componentType()));
2800*c8dee2aaSAndroid Build Coastguard Worker     const Type& srcColumnType = srcType.componentType().toCompound(fContext, srcType.rows(), 1);
2801*c8dee2aaSAndroid Build Coastguard Worker     const Type& dstColumnType = dstType.componentType().toCompound(fContext, dstType.rows(), 1);
2802*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(dstType.componentType().isFloat());
2803*c8dee2aaSAndroid Build Coastguard Worker     SpvId dstColumnTypeId = this->getType(dstColumnType);
2804*c8dee2aaSAndroid Build Coastguard Worker     const SpvId zeroId = this->writeLiteral(0.0, dstType.componentType());
2805*c8dee2aaSAndroid Build Coastguard Worker     const SpvId oneId = this->writeLiteral(1.0, dstType.componentType());
2806*c8dee2aaSAndroid Build Coastguard Worker 
2807*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, SpvId> columns;
2808*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < dstType.columns(); i++) {
2809*c8dee2aaSAndroid Build Coastguard Worker         if (i < srcType.columns()) {
2810*c8dee2aaSAndroid Build Coastguard Worker             // we're still inside the src matrix, copy the column
2811*c8dee2aaSAndroid Build Coastguard Worker             SpvId srcColumn = this->writeOpCompositeExtract(srcColumnType, src, i, out);
2812*c8dee2aaSAndroid Build Coastguard Worker             SpvId dstColumn;
2813*c8dee2aaSAndroid Build Coastguard Worker             if (srcType.rows() == dstType.rows()) {
2814*c8dee2aaSAndroid Build Coastguard Worker                 // columns are equal size, don't need to do anything
2815*c8dee2aaSAndroid Build Coastguard Worker                 dstColumn = srcColumn;
2816*c8dee2aaSAndroid Build Coastguard Worker             }
2817*c8dee2aaSAndroid Build Coastguard Worker             else if (dstType.rows() > srcType.rows()) {
2818*c8dee2aaSAndroid Build Coastguard Worker                 // dst column is bigger, need to zero-pad it
2819*c8dee2aaSAndroid Build Coastguard Worker                 STArray<4, SpvId> values;
2820*c8dee2aaSAndroid Build Coastguard Worker                 values.push_back(srcColumn);
2821*c8dee2aaSAndroid Build Coastguard Worker                 for (int j = srcType.rows(); j < dstType.rows(); ++j) {
2822*c8dee2aaSAndroid Build Coastguard Worker                     values.push_back((i == j) ? oneId : zeroId);
2823*c8dee2aaSAndroid Build Coastguard Worker                 }
2824*c8dee2aaSAndroid Build Coastguard Worker                 dstColumn = this->writeOpCompositeConstruct(dstColumnType, values, out);
2825*c8dee2aaSAndroid Build Coastguard Worker             }
2826*c8dee2aaSAndroid Build Coastguard Worker             else {
2827*c8dee2aaSAndroid Build Coastguard Worker                 // dst column is smaller, need to swizzle the src column
2828*c8dee2aaSAndroid Build Coastguard Worker                 dstColumn = this->nextId(&dstType);
2829*c8dee2aaSAndroid Build Coastguard Worker                 this->writeOpCode(SpvOpVectorShuffle, 5 + dstType.rows(), out);
2830*c8dee2aaSAndroid Build Coastguard Worker                 this->writeWord(dstColumnTypeId, out);
2831*c8dee2aaSAndroid Build Coastguard Worker                 this->writeWord(dstColumn, out);
2832*c8dee2aaSAndroid Build Coastguard Worker                 this->writeWord(srcColumn, out);
2833*c8dee2aaSAndroid Build Coastguard Worker                 this->writeWord(srcColumn, out);
2834*c8dee2aaSAndroid Build Coastguard Worker                 for (int j = 0; j < dstType.rows(); j++) {
2835*c8dee2aaSAndroid Build Coastguard Worker                     this->writeWord(j, out);
2836*c8dee2aaSAndroid Build Coastguard Worker                 }
2837*c8dee2aaSAndroid Build Coastguard Worker             }
2838*c8dee2aaSAndroid Build Coastguard Worker             columns.push_back(dstColumn);
2839*c8dee2aaSAndroid Build Coastguard Worker         } else {
2840*c8dee2aaSAndroid Build Coastguard Worker             // we're past the end of the src matrix, need to synthesize an identity-matrix column
2841*c8dee2aaSAndroid Build Coastguard Worker             STArray<4, SpvId> values;
2842*c8dee2aaSAndroid Build Coastguard Worker             for (int j = 0; j < dstType.rows(); ++j) {
2843*c8dee2aaSAndroid Build Coastguard Worker                 values.push_back((i == j) ? oneId : zeroId);
2844*c8dee2aaSAndroid Build Coastguard Worker             }
2845*c8dee2aaSAndroid Build Coastguard Worker             columns.push_back(this->writeOpCompositeConstruct(dstColumnType, values, out));
2846*c8dee2aaSAndroid Build Coastguard Worker         }
2847*c8dee2aaSAndroid Build Coastguard Worker     }
2848*c8dee2aaSAndroid Build Coastguard Worker 
2849*c8dee2aaSAndroid Build Coastguard Worker     return this->writeOpCompositeConstruct(dstType, columns, out);
2850*c8dee2aaSAndroid Build Coastguard Worker }
2851*c8dee2aaSAndroid Build Coastguard Worker 
addColumnEntry(const Type & columnType,TArray<SpvId> * currentColumn,TArray<SpvId> * columnIds,int rows,SpvId entry,OutputStream & out)2852*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::addColumnEntry(const Type& columnType,
2853*c8dee2aaSAndroid Build Coastguard Worker                                         TArray<SpvId>* currentColumn,
2854*c8dee2aaSAndroid Build Coastguard Worker                                         TArray<SpvId>* columnIds,
2855*c8dee2aaSAndroid Build Coastguard Worker                                         int rows,
2856*c8dee2aaSAndroid Build Coastguard Worker                                         SpvId entry,
2857*c8dee2aaSAndroid Build Coastguard Worker                                         OutputStream& out) {
2858*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(currentColumn->size() < rows);
2859*c8dee2aaSAndroid Build Coastguard Worker     currentColumn->push_back(entry);
2860*c8dee2aaSAndroid Build Coastguard Worker     if (currentColumn->size() == rows) {
2861*c8dee2aaSAndroid Build Coastguard Worker         // Synthesize this column into a vector.
2862*c8dee2aaSAndroid Build Coastguard Worker         SpvId columnId = this->writeOpCompositeConstruct(columnType, *currentColumn, out);
2863*c8dee2aaSAndroid Build Coastguard Worker         columnIds->push_back(columnId);
2864*c8dee2aaSAndroid Build Coastguard Worker         currentColumn->clear();
2865*c8dee2aaSAndroid Build Coastguard Worker     }
2866*c8dee2aaSAndroid Build Coastguard Worker }
2867*c8dee2aaSAndroid Build Coastguard Worker 
writeMatrixConstructor(const ConstructorCompound & c,OutputStream & out)2868*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeMatrixConstructor(const ConstructorCompound& c, OutputStream& out) {
2869*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = c.type();
2870*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type.isMatrix());
2871*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!c.arguments().empty());
2872*c8dee2aaSAndroid Build Coastguard Worker     const Type& arg0Type = c.arguments()[0]->type();
2873*c8dee2aaSAndroid Build Coastguard Worker     // go ahead and write the arguments so we don't try to write new instructions in the middle of
2874*c8dee2aaSAndroid Build Coastguard Worker     // an instruction
2875*c8dee2aaSAndroid Build Coastguard Worker     STArray<16, SpvId> arguments;
2876*c8dee2aaSAndroid Build Coastguard Worker     for (const std::unique_ptr<Expression>& arg : c.arguments()) {
2877*c8dee2aaSAndroid Build Coastguard Worker         arguments.push_back(this->writeExpression(*arg, out));
2878*c8dee2aaSAndroid Build Coastguard Worker     }
2879*c8dee2aaSAndroid Build Coastguard Worker 
2880*c8dee2aaSAndroid Build Coastguard Worker     if (arguments.size() == 1 && arg0Type.isVector()) {
2881*c8dee2aaSAndroid Build Coastguard Worker         // Special-case handling of float4 -> mat2x2.
2882*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(type.rows() == 2 && type.columns() == 2);
2883*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(arg0Type.columns() == 4);
2884*c8dee2aaSAndroid Build Coastguard Worker         SpvId v[4];
2885*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < 4; ++i) {
2886*c8dee2aaSAndroid Build Coastguard Worker             v[i] = this->writeOpCompositeExtract(type.componentType(), arguments[0], i, out);
2887*c8dee2aaSAndroid Build Coastguard Worker         }
2888*c8dee2aaSAndroid Build Coastguard Worker         const Type& vecType = type.columnType(fContext);
2889*c8dee2aaSAndroid Build Coastguard Worker         SpvId v0v1 = this->writeOpCompositeConstruct(vecType, {v[0], v[1]}, out);
2890*c8dee2aaSAndroid Build Coastguard Worker         SpvId v2v3 = this->writeOpCompositeConstruct(vecType, {v[2], v[3]}, out);
2891*c8dee2aaSAndroid Build Coastguard Worker         return this->writeOpCompositeConstruct(type, {v0v1, v2v3}, out);
2892*c8dee2aaSAndroid Build Coastguard Worker     }
2893*c8dee2aaSAndroid Build Coastguard Worker 
2894*c8dee2aaSAndroid Build Coastguard Worker     int rows = type.rows();
2895*c8dee2aaSAndroid Build Coastguard Worker     const Type& columnType = type.columnType(fContext);
2896*c8dee2aaSAndroid Build Coastguard Worker     // SpvIds of completed columns of the matrix.
2897*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, SpvId> columnIds;
2898*c8dee2aaSAndroid Build Coastguard Worker     // SpvIds of scalars we have written to the current column so far.
2899*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, SpvId> currentColumn;
2900*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < arguments.size(); i++) {
2901*c8dee2aaSAndroid Build Coastguard Worker         const Type& argType = c.arguments()[i]->type();
2902*c8dee2aaSAndroid Build Coastguard Worker         if (currentColumn.empty() && argType.isVector() && argType.columns() == rows) {
2903*c8dee2aaSAndroid Build Coastguard Worker             // This vector is a complete matrix column by itself and can be used as-is.
2904*c8dee2aaSAndroid Build Coastguard Worker             columnIds.push_back(arguments[i]);
2905*c8dee2aaSAndroid Build Coastguard Worker         } else if (argType.columns() == 1) {
2906*c8dee2aaSAndroid Build Coastguard Worker             // This argument is a lone scalar and can be added to the current column as-is.
2907*c8dee2aaSAndroid Build Coastguard Worker             this->addColumnEntry(columnType, &currentColumn, &columnIds, rows, arguments[i], out);
2908*c8dee2aaSAndroid Build Coastguard Worker         } else {
2909*c8dee2aaSAndroid Build Coastguard Worker             // This argument needs to be decomposed into its constituent scalars.
2910*c8dee2aaSAndroid Build Coastguard Worker             for (int j = 0; j < argType.columns(); ++j) {
2911*c8dee2aaSAndroid Build Coastguard Worker                 SpvId swizzle = this->writeOpCompositeExtract(argType.componentType(),
2912*c8dee2aaSAndroid Build Coastguard Worker                                                               arguments[i], j, out);
2913*c8dee2aaSAndroid Build Coastguard Worker                 this->addColumnEntry(columnType, &currentColumn, &columnIds, rows, swizzle, out);
2914*c8dee2aaSAndroid Build Coastguard Worker             }
2915*c8dee2aaSAndroid Build Coastguard Worker         }
2916*c8dee2aaSAndroid Build Coastguard Worker     }
2917*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(columnIds.size() == type.columns());
2918*c8dee2aaSAndroid Build Coastguard Worker     return this->writeOpCompositeConstruct(type, columnIds, out);
2919*c8dee2aaSAndroid Build Coastguard Worker }
2920*c8dee2aaSAndroid Build Coastguard Worker 
writeConstructorCompound(const ConstructorCompound & c,OutputStream & out)2921*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeConstructorCompound(const ConstructorCompound& c,
2922*c8dee2aaSAndroid Build Coastguard Worker                                                    OutputStream& out) {
2923*c8dee2aaSAndroid Build Coastguard Worker     return c.type().isMatrix() ? this->writeMatrixConstructor(c, out)
2924*c8dee2aaSAndroid Build Coastguard Worker                                : this->writeVectorConstructor(c, out);
2925*c8dee2aaSAndroid Build Coastguard Worker }
2926*c8dee2aaSAndroid Build Coastguard Worker 
writeVectorConstructor(const ConstructorCompound & c,OutputStream & out)2927*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeVectorConstructor(const ConstructorCompound& c, OutputStream& out) {
2928*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = c.type();
2929*c8dee2aaSAndroid Build Coastguard Worker     const Type& componentType = type.componentType();
2930*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type.isVector());
2931*c8dee2aaSAndroid Build Coastguard Worker 
2932*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, SpvId> arguments;
2933*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < c.arguments().size(); i++) {
2934*c8dee2aaSAndroid Build Coastguard Worker         const Type& argType = c.arguments()[i]->type();
2935*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(componentType.numberKind() == argType.componentType().numberKind());
2936*c8dee2aaSAndroid Build Coastguard Worker 
2937*c8dee2aaSAndroid Build Coastguard Worker         SpvId arg = this->writeExpression(*c.arguments()[i], out);
2938*c8dee2aaSAndroid Build Coastguard Worker         if (argType.isMatrix()) {
2939*c8dee2aaSAndroid Build Coastguard Worker             // CompositeConstruct cannot take a 2x2 matrix as an input, so we need to extract out
2940*c8dee2aaSAndroid Build Coastguard Worker             // each scalar separately.
2941*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(argType.rows() == 2);
2942*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(argType.columns() == 2);
2943*c8dee2aaSAndroid Build Coastguard Worker             for (int j = 0; j < 4; ++j) {
2944*c8dee2aaSAndroid Build Coastguard Worker                 arguments.push_back(this->writeOpCompositeExtract(componentType, arg,
2945*c8dee2aaSAndroid Build Coastguard Worker                                                                   j / 2, j % 2, out));
2946*c8dee2aaSAndroid Build Coastguard Worker             }
2947*c8dee2aaSAndroid Build Coastguard Worker         } else if (argType.isVector()) {
2948*c8dee2aaSAndroid Build Coastguard Worker             // There's a bug in the Intel Vulkan driver where OpCompositeConstruct doesn't handle
2949*c8dee2aaSAndroid Build Coastguard Worker             // vector arguments at all, so we always extract each vector component and pass them
2950*c8dee2aaSAndroid Build Coastguard Worker             // into OpCompositeConstruct individually.
2951*c8dee2aaSAndroid Build Coastguard Worker             for (int j = 0; j < argType.columns(); j++) {
2952*c8dee2aaSAndroid Build Coastguard Worker                 arguments.push_back(this->writeOpCompositeExtract(componentType, arg, j, out));
2953*c8dee2aaSAndroid Build Coastguard Worker             }
2954*c8dee2aaSAndroid Build Coastguard Worker         } else {
2955*c8dee2aaSAndroid Build Coastguard Worker             arguments.push_back(arg);
2956*c8dee2aaSAndroid Build Coastguard Worker         }
2957*c8dee2aaSAndroid Build Coastguard Worker     }
2958*c8dee2aaSAndroid Build Coastguard Worker 
2959*c8dee2aaSAndroid Build Coastguard Worker     return this->writeOpCompositeConstruct(type, arguments, out);
2960*c8dee2aaSAndroid Build Coastguard Worker }
2961*c8dee2aaSAndroid Build Coastguard Worker 
writeConstructorSplat(const ConstructorSplat & c,OutputStream & out)2962*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeConstructorSplat(const ConstructorSplat& c, OutputStream& out) {
2963*c8dee2aaSAndroid Build Coastguard Worker     // Write the splat argument as a scalar, then splat it.
2964*c8dee2aaSAndroid Build Coastguard Worker     SpvId argument = this->writeExpression(*c.argument(), out);
2965*c8dee2aaSAndroid Build Coastguard Worker     return this->splat(c.type(), argument, out);
2966*c8dee2aaSAndroid Build Coastguard Worker }
2967*c8dee2aaSAndroid Build Coastguard Worker 
writeCompositeConstructor(const AnyConstructor & c,OutputStream & out)2968*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeCompositeConstructor(const AnyConstructor& c, OutputStream& out) {
2969*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(c.type().isArray() || c.type().isStruct());
2970*c8dee2aaSAndroid Build Coastguard Worker     auto ctorArgs = c.argumentSpan();
2971*c8dee2aaSAndroid Build Coastguard Worker 
2972*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, SpvId> arguments;
2973*c8dee2aaSAndroid Build Coastguard Worker     for (const std::unique_ptr<Expression>& arg : ctorArgs) {
2974*c8dee2aaSAndroid Build Coastguard Worker         arguments.push_back(this->writeExpression(*arg, out));
2975*c8dee2aaSAndroid Build Coastguard Worker     }
2976*c8dee2aaSAndroid Build Coastguard Worker 
2977*c8dee2aaSAndroid Build Coastguard Worker     return this->writeOpCompositeConstruct(c.type(), arguments, out);
2978*c8dee2aaSAndroid Build Coastguard Worker }
2979*c8dee2aaSAndroid Build Coastguard Worker 
writeConstructorScalarCast(const ConstructorScalarCast & c,OutputStream & out)2980*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeConstructorScalarCast(const ConstructorScalarCast& c,
2981*c8dee2aaSAndroid Build Coastguard Worker                                                      OutputStream& out) {
2982*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = c.type();
2983*c8dee2aaSAndroid Build Coastguard Worker     if (type.componentType().numberKind() == c.argument()->type().componentType().numberKind()) {
2984*c8dee2aaSAndroid Build Coastguard Worker         return this->writeExpression(*c.argument(), out);
2985*c8dee2aaSAndroid Build Coastguard Worker     }
2986*c8dee2aaSAndroid Build Coastguard Worker 
2987*c8dee2aaSAndroid Build Coastguard Worker     const Expression& ctorExpr = *c.argument();
2988*c8dee2aaSAndroid Build Coastguard Worker     SpvId expressionId = this->writeExpression(ctorExpr, out);
2989*c8dee2aaSAndroid Build Coastguard Worker     return this->castScalarToType(expressionId, ctorExpr.type(), type, out);
2990*c8dee2aaSAndroid Build Coastguard Worker }
2991*c8dee2aaSAndroid Build Coastguard Worker 
writeConstructorCompoundCast(const ConstructorCompoundCast & c,OutputStream & out)2992*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeConstructorCompoundCast(const ConstructorCompoundCast& c,
2993*c8dee2aaSAndroid Build Coastguard Worker                                                        OutputStream& out) {
2994*c8dee2aaSAndroid Build Coastguard Worker     const Type& ctorType = c.type();
2995*c8dee2aaSAndroid Build Coastguard Worker     const Type& argType = c.argument()->type();
2996*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(ctorType.isVector() || ctorType.isMatrix());
2997*c8dee2aaSAndroid Build Coastguard Worker 
2998*c8dee2aaSAndroid Build Coastguard Worker     // Write the composite that we are casting. If the actual type matches, we are done.
2999*c8dee2aaSAndroid Build Coastguard Worker     SpvId compositeId = this->writeExpression(*c.argument(), out);
3000*c8dee2aaSAndroid Build Coastguard Worker     if (ctorType.componentType().numberKind() == argType.componentType().numberKind()) {
3001*c8dee2aaSAndroid Build Coastguard Worker         return compositeId;
3002*c8dee2aaSAndroid Build Coastguard Worker     }
3003*c8dee2aaSAndroid Build Coastguard Worker 
3004*c8dee2aaSAndroid Build Coastguard Worker     // writeMatrixCopy can cast matrices to a different type.
3005*c8dee2aaSAndroid Build Coastguard Worker     if (ctorType.isMatrix()) {
3006*c8dee2aaSAndroid Build Coastguard Worker         return this->writeMatrixCopy(compositeId, argType, ctorType, out);
3007*c8dee2aaSAndroid Build Coastguard Worker     }
3008*c8dee2aaSAndroid Build Coastguard Worker 
3009*c8dee2aaSAndroid Build Coastguard Worker     // SPIR-V doesn't support vector(vector-of-different-type) directly, so we need to extract the
3010*c8dee2aaSAndroid Build Coastguard Worker     // components and convert each one manually.
3011*c8dee2aaSAndroid Build Coastguard Worker     const Type& srcType = argType.componentType();
3012*c8dee2aaSAndroid Build Coastguard Worker     const Type& dstType = ctorType.componentType();
3013*c8dee2aaSAndroid Build Coastguard Worker 
3014*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, SpvId> arguments;
3015*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < argType.columns(); ++index) {
3016*c8dee2aaSAndroid Build Coastguard Worker         SpvId componentId = this->writeOpCompositeExtract(srcType, compositeId, index, out);
3017*c8dee2aaSAndroid Build Coastguard Worker         arguments.push_back(this->castScalarToType(componentId, srcType, dstType, out));
3018*c8dee2aaSAndroid Build Coastguard Worker     }
3019*c8dee2aaSAndroid Build Coastguard Worker 
3020*c8dee2aaSAndroid Build Coastguard Worker     return this->writeOpCompositeConstruct(ctorType, arguments, out);
3021*c8dee2aaSAndroid Build Coastguard Worker }
3022*c8dee2aaSAndroid Build Coastguard Worker 
writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix & c,OutputStream & out)3023*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c,
3024*c8dee2aaSAndroid Build Coastguard Worker                                                          OutputStream& out) {
3025*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = c.type();
3026*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type.isMatrix());
3027*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(c.argument()->type().isScalar());
3028*c8dee2aaSAndroid Build Coastguard Worker 
3029*c8dee2aaSAndroid Build Coastguard Worker     // Write out the scalar argument.
3030*c8dee2aaSAndroid Build Coastguard Worker     SpvId diagonal = this->writeExpression(*c.argument(), out);
3031*c8dee2aaSAndroid Build Coastguard Worker 
3032*c8dee2aaSAndroid Build Coastguard Worker     // Build the diagonal matrix.
3033*c8dee2aaSAndroid Build Coastguard Worker     SpvId zeroId = this->writeLiteral(0.0, *fContext.fTypes.fFloat);
3034*c8dee2aaSAndroid Build Coastguard Worker 
3035*c8dee2aaSAndroid Build Coastguard Worker     const Type& vecType = type.columnType(fContext);
3036*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, SpvId> columnIds;
3037*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, SpvId> arguments;
3038*c8dee2aaSAndroid Build Coastguard Worker     arguments.resize(type.rows());
3039*c8dee2aaSAndroid Build Coastguard Worker     for (int column = 0; column < type.columns(); column++) {
3040*c8dee2aaSAndroid Build Coastguard Worker         for (int row = 0; row < type.rows(); row++) {
3041*c8dee2aaSAndroid Build Coastguard Worker             arguments[row] = (row == column) ? diagonal : zeroId;
3042*c8dee2aaSAndroid Build Coastguard Worker         }
3043*c8dee2aaSAndroid Build Coastguard Worker         columnIds.push_back(this->writeOpCompositeConstruct(vecType, arguments, out));
3044*c8dee2aaSAndroid Build Coastguard Worker     }
3045*c8dee2aaSAndroid Build Coastguard Worker     return this->writeOpCompositeConstruct(type, columnIds, out);
3046*c8dee2aaSAndroid Build Coastguard Worker }
3047*c8dee2aaSAndroid Build Coastguard Worker 
writeConstructorMatrixResize(const ConstructorMatrixResize & c,OutputStream & out)3048*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeConstructorMatrixResize(const ConstructorMatrixResize& c,
3049*c8dee2aaSAndroid Build Coastguard Worker                                                        OutputStream& out) {
3050*c8dee2aaSAndroid Build Coastguard Worker     // Write the input matrix.
3051*c8dee2aaSAndroid Build Coastguard Worker     SpvId argument = this->writeExpression(*c.argument(), out);
3052*c8dee2aaSAndroid Build Coastguard Worker 
3053*c8dee2aaSAndroid Build Coastguard Worker     // Use matrix-copy to resize the input matrix to its new size.
3054*c8dee2aaSAndroid Build Coastguard Worker     return this->writeMatrixCopy(argument, c.argument()->type(), c.type(), out);
3055*c8dee2aaSAndroid Build Coastguard Worker }
3056*c8dee2aaSAndroid Build Coastguard Worker 
get_storage_class_for_global_variable(const Variable & var,StorageClass fallbackStorageClass)3057*c8dee2aaSAndroid Build Coastguard Worker static StorageClass get_storage_class_for_global_variable(
3058*c8dee2aaSAndroid Build Coastguard Worker         const Variable& var, StorageClass fallbackStorageClass) {
3059*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(var.storage() == Variable::Storage::kGlobal);
3060*c8dee2aaSAndroid Build Coastguard Worker 
3061*c8dee2aaSAndroid Build Coastguard Worker     if (var.type().typeKind() == Type::TypeKind::kSampler ||
3062*c8dee2aaSAndroid Build Coastguard Worker         var.type().typeKind() == Type::TypeKind::kSeparateSampler ||
3063*c8dee2aaSAndroid Build Coastguard Worker         var.type().typeKind() == Type::TypeKind::kTexture) {
3064*c8dee2aaSAndroid Build Coastguard Worker         return StorageClass::kUniformConstant;
3065*c8dee2aaSAndroid Build Coastguard Worker     }
3066*c8dee2aaSAndroid Build Coastguard Worker 
3067*c8dee2aaSAndroid Build Coastguard Worker     const Layout& layout = var.layout();
3068*c8dee2aaSAndroid Build Coastguard Worker     ModifierFlags flags = var.modifierFlags();
3069*c8dee2aaSAndroid Build Coastguard Worker     if (flags & ModifierFlag::kIn) {
3070*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!(layout.fFlags & LayoutFlag::kPushConstant));
3071*c8dee2aaSAndroid Build Coastguard Worker         return StorageClass::kInput;
3072*c8dee2aaSAndroid Build Coastguard Worker     }
3073*c8dee2aaSAndroid Build Coastguard Worker     if (flags & ModifierFlag::kOut) {
3074*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!(layout.fFlags & LayoutFlag::kPushConstant));
3075*c8dee2aaSAndroid Build Coastguard Worker         return StorageClass::kOutput;
3076*c8dee2aaSAndroid Build Coastguard Worker     }
3077*c8dee2aaSAndroid Build Coastguard Worker     if (flags.isUniform()) {
3078*c8dee2aaSAndroid Build Coastguard Worker         if (layout.fFlags & LayoutFlag::kPushConstant) {
3079*c8dee2aaSAndroid Build Coastguard Worker             return StorageClass::kPushConstant;
3080*c8dee2aaSAndroid Build Coastguard Worker         }
3081*c8dee2aaSAndroid Build Coastguard Worker         return StorageClass::kUniform;
3082*c8dee2aaSAndroid Build Coastguard Worker     }
3083*c8dee2aaSAndroid Build Coastguard Worker     if (flags.isBuffer()) {
3084*c8dee2aaSAndroid Build Coastguard Worker         return StorageClass::kStorageBuffer;
3085*c8dee2aaSAndroid Build Coastguard Worker     }
3086*c8dee2aaSAndroid Build Coastguard Worker     if (flags.isWorkgroup()) {
3087*c8dee2aaSAndroid Build Coastguard Worker         return StorageClass::kWorkgroup;
3088*c8dee2aaSAndroid Build Coastguard Worker     }
3089*c8dee2aaSAndroid Build Coastguard Worker     return fallbackStorageClass;
3090*c8dee2aaSAndroid Build Coastguard Worker }
3091*c8dee2aaSAndroid Build Coastguard Worker 
getStorageClass(const Expression & expr)3092*c8dee2aaSAndroid Build Coastguard Worker StorageClass SPIRVCodeGenerator::getStorageClass(const Expression& expr) {
3093*c8dee2aaSAndroid Build Coastguard Worker     switch (expr.kind()) {
3094*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kVariableReference: {
3095*c8dee2aaSAndroid Build Coastguard Worker             const Variable& var = *expr.as<VariableReference>().variable();
3096*c8dee2aaSAndroid Build Coastguard Worker             if (fActiveSpecialization) {
3097*c8dee2aaSAndroid Build Coastguard Worker                 const Expression** specializedExpr = fActiveSpecialization->find(&var);
3098*c8dee2aaSAndroid Build Coastguard Worker                 if (specializedExpr && (*specializedExpr)->is<FieldAccess>()) {
3099*c8dee2aaSAndroid Build Coastguard Worker                     return this->getStorageClass(**specializedExpr);
3100*c8dee2aaSAndroid Build Coastguard Worker                 }
3101*c8dee2aaSAndroid Build Coastguard Worker             }
3102*c8dee2aaSAndroid Build Coastguard Worker             if (var.storage() != Variable::Storage::kGlobal) {
3103*c8dee2aaSAndroid Build Coastguard Worker                 return StorageClass::kFunction;
3104*c8dee2aaSAndroid Build Coastguard Worker             }
3105*c8dee2aaSAndroid Build Coastguard Worker             return get_storage_class_for_global_variable(var, StorageClass::kPrivate);
3106*c8dee2aaSAndroid Build Coastguard Worker         }
3107*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kFieldAccess:
3108*c8dee2aaSAndroid Build Coastguard Worker             return this->getStorageClass(*expr.as<FieldAccess>().base());
3109*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kIndex:
3110*c8dee2aaSAndroid Build Coastguard Worker             return this->getStorageClass(*expr.as<IndexExpression>().base());
3111*c8dee2aaSAndroid Build Coastguard Worker         default:
3112*c8dee2aaSAndroid Build Coastguard Worker             return StorageClass::kFunction;
3113*c8dee2aaSAndroid Build Coastguard Worker     }
3114*c8dee2aaSAndroid Build Coastguard Worker }
3115*c8dee2aaSAndroid Build Coastguard Worker 
getAccessChain(const Expression & expr,OutputStream & out)3116*c8dee2aaSAndroid Build Coastguard Worker TArray<SpvId> SPIRVCodeGenerator::getAccessChain(const Expression& expr, OutputStream& out) {
3117*c8dee2aaSAndroid Build Coastguard Worker     switch (expr.kind()) {
3118*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kIndex: {
3119*c8dee2aaSAndroid Build Coastguard Worker             const IndexExpression& indexExpr = expr.as<IndexExpression>();
3120*c8dee2aaSAndroid Build Coastguard Worker             if (indexExpr.base()->is<Swizzle>()) {
3121*c8dee2aaSAndroid Build Coastguard Worker                 // Access chains don't directly support dynamically indexing into a swizzle, but we
3122*c8dee2aaSAndroid Build Coastguard Worker                 // can rewrite them into a supported form.
3123*c8dee2aaSAndroid Build Coastguard Worker                 return this->getAccessChain(*Transform::RewriteIndexedSwizzle(fContext, indexExpr),
3124*c8dee2aaSAndroid Build Coastguard Worker                                             out);
3125*c8dee2aaSAndroid Build Coastguard Worker             }
3126*c8dee2aaSAndroid Build Coastguard Worker             // All other index-expressions can be represented as typical access chains.
3127*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> chain = this->getAccessChain(*indexExpr.base(), out);
3128*c8dee2aaSAndroid Build Coastguard Worker             chain.push_back(this->writeExpression(*indexExpr.index(), out));
3129*c8dee2aaSAndroid Build Coastguard Worker             return chain;
3130*c8dee2aaSAndroid Build Coastguard Worker         }
3131*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kFieldAccess: {
3132*c8dee2aaSAndroid Build Coastguard Worker             const FieldAccess& fieldExpr = expr.as<FieldAccess>();
3133*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> chain = this->getAccessChain(*fieldExpr.base(), out);
3134*c8dee2aaSAndroid Build Coastguard Worker             chain.push_back(this->writeLiteral(fieldExpr.fieldIndex(), *fContext.fTypes.fInt));
3135*c8dee2aaSAndroid Build Coastguard Worker             return chain;
3136*c8dee2aaSAndroid Build Coastguard Worker         }
3137*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kVariableReference: {
3138*c8dee2aaSAndroid Build Coastguard Worker             if (fActiveSpecialization) {
3139*c8dee2aaSAndroid Build Coastguard Worker                 const Expression** specializedFieldIndex =
3140*c8dee2aaSAndroid Build Coastguard Worker                         fActiveSpecialization->find(expr.as<VariableReference>().variable());
3141*c8dee2aaSAndroid Build Coastguard Worker                 if (specializedFieldIndex && (*specializedFieldIndex)->is<FieldAccess>()) {
3142*c8dee2aaSAndroid Build Coastguard Worker                     return this->getAccessChain(**specializedFieldIndex, out);
3143*c8dee2aaSAndroid Build Coastguard Worker                 }
3144*c8dee2aaSAndroid Build Coastguard Worker             }
3145*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
3146*c8dee2aaSAndroid Build Coastguard Worker         }
3147*c8dee2aaSAndroid Build Coastguard Worker         default: {
3148*c8dee2aaSAndroid Build Coastguard Worker             SpvId id = this->getLValue(expr, out)->getPointer();
3149*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(id != NA);
3150*c8dee2aaSAndroid Build Coastguard Worker             return TArray<SpvId>{id};
3151*c8dee2aaSAndroid Build Coastguard Worker         }
3152*c8dee2aaSAndroid Build Coastguard Worker     }
3153*c8dee2aaSAndroid Build Coastguard Worker     SkUNREACHABLE;
3154*c8dee2aaSAndroid Build Coastguard Worker }
3155*c8dee2aaSAndroid Build Coastguard Worker 
3156*c8dee2aaSAndroid Build Coastguard Worker class PointerLValue : public SPIRVCodeGenerator::LValue {
3157*c8dee2aaSAndroid Build Coastguard Worker public:
PointerLValue(SPIRVCodeGenerator & gen,SpvId pointer,bool isMemoryObject,SpvId type,SPIRVCodeGenerator::Precision precision,StorageClass storageClass)3158*c8dee2aaSAndroid Build Coastguard Worker     PointerLValue(SPIRVCodeGenerator& gen, SpvId pointer, bool isMemoryObject, SpvId type,
3159*c8dee2aaSAndroid Build Coastguard Worker                   SPIRVCodeGenerator::Precision precision, StorageClass storageClass)
3160*c8dee2aaSAndroid Build Coastguard Worker     : fGen(gen)
3161*c8dee2aaSAndroid Build Coastguard Worker     , fPointer(pointer)
3162*c8dee2aaSAndroid Build Coastguard Worker     , fIsMemoryObject(isMemoryObject)
3163*c8dee2aaSAndroid Build Coastguard Worker     , fType(type)
3164*c8dee2aaSAndroid Build Coastguard Worker     , fPrecision(precision)
3165*c8dee2aaSAndroid Build Coastguard Worker     , fStorageClass(storageClass) {}
3166*c8dee2aaSAndroid Build Coastguard Worker 
getPointer()3167*c8dee2aaSAndroid Build Coastguard Worker     SpvId getPointer() override {
3168*c8dee2aaSAndroid Build Coastguard Worker         return fPointer;
3169*c8dee2aaSAndroid Build Coastguard Worker     }
3170*c8dee2aaSAndroid Build Coastguard Worker 
isMemoryObjectPointer() const3171*c8dee2aaSAndroid Build Coastguard Worker     bool isMemoryObjectPointer() const override {
3172*c8dee2aaSAndroid Build Coastguard Worker         return fIsMemoryObject;
3173*c8dee2aaSAndroid Build Coastguard Worker     }
3174*c8dee2aaSAndroid Build Coastguard Worker 
storageClass() const3175*c8dee2aaSAndroid Build Coastguard Worker     StorageClass storageClass() const override {
3176*c8dee2aaSAndroid Build Coastguard Worker         return fStorageClass;
3177*c8dee2aaSAndroid Build Coastguard Worker     }
3178*c8dee2aaSAndroid Build Coastguard Worker 
load(OutputStream & out)3179*c8dee2aaSAndroid Build Coastguard Worker     SpvId load(OutputStream& out) override {
3180*c8dee2aaSAndroid Build Coastguard Worker         return fGen.writeOpLoad(fType, fPrecision, fPointer, out);
3181*c8dee2aaSAndroid Build Coastguard Worker     }
3182*c8dee2aaSAndroid Build Coastguard Worker 
store(SpvId value,OutputStream & out)3183*c8dee2aaSAndroid Build Coastguard Worker     void store(SpvId value, OutputStream& out) override {
3184*c8dee2aaSAndroid Build Coastguard Worker         if (!fIsMemoryObject) {
3185*c8dee2aaSAndroid Build Coastguard Worker             // We are going to write into an access chain; this could represent one component of a
3186*c8dee2aaSAndroid Build Coastguard Worker             // vector, or one element of an array. This has the potential to invalidate other,
3187*c8dee2aaSAndroid Build Coastguard Worker             // *unknown* elements of our store cache. (e.g. if the store cache holds `%50 = myVec4`,
3188*c8dee2aaSAndroid Build Coastguard Worker             // and we store `%60 = myVec4.z`, this invalidates the cached value for %50.) To avoid
3189*c8dee2aaSAndroid Build Coastguard Worker             // relying on stale data, reset the store cache entirely when this happens.
3190*c8dee2aaSAndroid Build Coastguard Worker             fGen.fStoreCache.reset();
3191*c8dee2aaSAndroid Build Coastguard Worker         }
3192*c8dee2aaSAndroid Build Coastguard Worker 
3193*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeOpStore(fStorageClass, fPointer, value, out);
3194*c8dee2aaSAndroid Build Coastguard Worker     }
3195*c8dee2aaSAndroid Build Coastguard Worker 
3196*c8dee2aaSAndroid Build Coastguard Worker private:
3197*c8dee2aaSAndroid Build Coastguard Worker     SPIRVCodeGenerator& fGen;
3198*c8dee2aaSAndroid Build Coastguard Worker     const SpvId fPointer;
3199*c8dee2aaSAndroid Build Coastguard Worker     const bool fIsMemoryObject;
3200*c8dee2aaSAndroid Build Coastguard Worker     const SpvId fType;
3201*c8dee2aaSAndroid Build Coastguard Worker     const SPIRVCodeGenerator::Precision fPrecision;
3202*c8dee2aaSAndroid Build Coastguard Worker     const StorageClass fStorageClass;
3203*c8dee2aaSAndroid Build Coastguard Worker };
3204*c8dee2aaSAndroid Build Coastguard Worker 
3205*c8dee2aaSAndroid Build Coastguard Worker class SwizzleLValue : public SPIRVCodeGenerator::LValue {
3206*c8dee2aaSAndroid Build Coastguard Worker public:
SwizzleLValue(SPIRVCodeGenerator & gen,SpvId vecPointer,const ComponentArray & components,const Type & baseType,const Type & swizzleType,StorageClass storageClass)3207*c8dee2aaSAndroid Build Coastguard Worker     SwizzleLValue(SPIRVCodeGenerator& gen, SpvId vecPointer, const ComponentArray& components,
3208*c8dee2aaSAndroid Build Coastguard Worker                   const Type& baseType, const Type& swizzleType, StorageClass storageClass)
3209*c8dee2aaSAndroid Build Coastguard Worker     : fGen(gen)
3210*c8dee2aaSAndroid Build Coastguard Worker     , fVecPointer(vecPointer)
3211*c8dee2aaSAndroid Build Coastguard Worker     , fComponents(components)
3212*c8dee2aaSAndroid Build Coastguard Worker     , fBaseType(&baseType)
3213*c8dee2aaSAndroid Build Coastguard Worker     , fSwizzleType(&swizzleType)
3214*c8dee2aaSAndroid Build Coastguard Worker     , fStorageClass(storageClass) {}
3215*c8dee2aaSAndroid Build Coastguard Worker 
applySwizzle(const ComponentArray & components,const Type & newType)3216*c8dee2aaSAndroid Build Coastguard Worker     bool applySwizzle(const ComponentArray& components, const Type& newType) override {
3217*c8dee2aaSAndroid Build Coastguard Worker         ComponentArray updatedSwizzle;
3218*c8dee2aaSAndroid Build Coastguard Worker         for (int8_t component : components) {
3219*c8dee2aaSAndroid Build Coastguard Worker             if (component < 0 || component >= fComponents.size()) {
3220*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAILF("swizzle accessed nonexistent component %d", (int)component);
3221*c8dee2aaSAndroid Build Coastguard Worker                 return false;
3222*c8dee2aaSAndroid Build Coastguard Worker             }
3223*c8dee2aaSAndroid Build Coastguard Worker             updatedSwizzle.push_back(fComponents[component]);
3224*c8dee2aaSAndroid Build Coastguard Worker         }
3225*c8dee2aaSAndroid Build Coastguard Worker         fComponents = updatedSwizzle;
3226*c8dee2aaSAndroid Build Coastguard Worker         fSwizzleType = &newType;
3227*c8dee2aaSAndroid Build Coastguard Worker         return true;
3228*c8dee2aaSAndroid Build Coastguard Worker     }
3229*c8dee2aaSAndroid Build Coastguard Worker 
storageClass() const3230*c8dee2aaSAndroid Build Coastguard Worker     StorageClass storageClass() const override {
3231*c8dee2aaSAndroid Build Coastguard Worker         return fStorageClass;
3232*c8dee2aaSAndroid Build Coastguard Worker     }
3233*c8dee2aaSAndroid Build Coastguard Worker 
load(OutputStream & out)3234*c8dee2aaSAndroid Build Coastguard Worker     SpvId load(OutputStream& out) override {
3235*c8dee2aaSAndroid Build Coastguard Worker         SpvId base = fGen.nextId(fBaseType);
3236*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeInstruction(SpvOpLoad, fGen.getType(*fBaseType), base, fVecPointer, out);
3237*c8dee2aaSAndroid Build Coastguard Worker         SpvId result = fGen.nextId(fBaseType);
3238*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeOpCode(SpvOpVectorShuffle, 5 + (int32_t) fComponents.size(), out);
3239*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeWord(fGen.getType(*fSwizzleType), out);
3240*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeWord(result, out);
3241*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeWord(base, out);
3242*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeWord(base, out);
3243*c8dee2aaSAndroid Build Coastguard Worker         for (int component : fComponents) {
3244*c8dee2aaSAndroid Build Coastguard Worker             fGen.writeWord(component, out);
3245*c8dee2aaSAndroid Build Coastguard Worker         }
3246*c8dee2aaSAndroid Build Coastguard Worker         return result;
3247*c8dee2aaSAndroid Build Coastguard Worker     }
3248*c8dee2aaSAndroid Build Coastguard Worker 
store(SpvId value,OutputStream & out)3249*c8dee2aaSAndroid Build Coastguard Worker     void store(SpvId value, OutputStream& out) override {
3250*c8dee2aaSAndroid Build Coastguard Worker         // use OpVectorShuffle to mix and match the vector components. We effectively create
3251*c8dee2aaSAndroid Build Coastguard Worker         // a virtual vector out of the concatenation of the left and right vectors, and then
3252*c8dee2aaSAndroid Build Coastguard Worker         // select components from this virtual vector to make the result vector. For
3253*c8dee2aaSAndroid Build Coastguard Worker         // instance, given:
3254*c8dee2aaSAndroid Build Coastguard Worker         // float3L = ...;
3255*c8dee2aaSAndroid Build Coastguard Worker         // float3R = ...;
3256*c8dee2aaSAndroid Build Coastguard Worker         // L.xz = R.xy;
3257*c8dee2aaSAndroid Build Coastguard Worker         // we end up with the virtual vector (L.x, L.y, L.z, R.x, R.y, R.z). Then we want
3258*c8dee2aaSAndroid Build Coastguard Worker         // our result vector to look like (R.x, L.y, R.y), so we need to select indices
3259*c8dee2aaSAndroid Build Coastguard Worker         // (3, 1, 4).
3260*c8dee2aaSAndroid Build Coastguard Worker         SpvId base = fGen.nextId(fBaseType);
3261*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeInstruction(SpvOpLoad, fGen.getType(*fBaseType), base, fVecPointer, out);
3262*c8dee2aaSAndroid Build Coastguard Worker         SpvId shuffle = fGen.nextId(fBaseType);
3263*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeOpCode(SpvOpVectorShuffle, 5 + fBaseType->columns(), out);
3264*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeWord(fGen.getType(*fBaseType), out);
3265*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeWord(shuffle, out);
3266*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeWord(base, out);
3267*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeWord(value, out);
3268*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < fBaseType->columns(); i++) {
3269*c8dee2aaSAndroid Build Coastguard Worker             // current offset into the virtual vector, defaults to pulling the unmodified
3270*c8dee2aaSAndroid Build Coastguard Worker             // value from the left side
3271*c8dee2aaSAndroid Build Coastguard Worker             int offset = i;
3272*c8dee2aaSAndroid Build Coastguard Worker             // check to see if we are writing this component
3273*c8dee2aaSAndroid Build Coastguard Worker             for (int j = 0; j < fComponents.size(); j++) {
3274*c8dee2aaSAndroid Build Coastguard Worker                 if (fComponents[j] == i) {
3275*c8dee2aaSAndroid Build Coastguard Worker                     // we're writing to this component, so adjust the offset to pull from
3276*c8dee2aaSAndroid Build Coastguard Worker                     // the correct component of the right side instead of preserving the
3277*c8dee2aaSAndroid Build Coastguard Worker                     // value from the left
3278*c8dee2aaSAndroid Build Coastguard Worker                     offset = (int) (j + fBaseType->columns());
3279*c8dee2aaSAndroid Build Coastguard Worker                     break;
3280*c8dee2aaSAndroid Build Coastguard Worker                 }
3281*c8dee2aaSAndroid Build Coastguard Worker             }
3282*c8dee2aaSAndroid Build Coastguard Worker             fGen.writeWord(offset, out);
3283*c8dee2aaSAndroid Build Coastguard Worker         }
3284*c8dee2aaSAndroid Build Coastguard Worker         fGen.writeOpStore(fStorageClass, fVecPointer, shuffle, out);
3285*c8dee2aaSAndroid Build Coastguard Worker     }
3286*c8dee2aaSAndroid Build Coastguard Worker 
3287*c8dee2aaSAndroid Build Coastguard Worker private:
3288*c8dee2aaSAndroid Build Coastguard Worker     SPIRVCodeGenerator& fGen;
3289*c8dee2aaSAndroid Build Coastguard Worker     const SpvId fVecPointer;
3290*c8dee2aaSAndroid Build Coastguard Worker     ComponentArray fComponents;
3291*c8dee2aaSAndroid Build Coastguard Worker     const Type* fBaseType;
3292*c8dee2aaSAndroid Build Coastguard Worker     const Type* fSwizzleType;
3293*c8dee2aaSAndroid Build Coastguard Worker     const StorageClass fStorageClass;
3294*c8dee2aaSAndroid Build Coastguard Worker };
3295*c8dee2aaSAndroid Build Coastguard Worker 
findUniformFieldIndex(const Variable & var) const3296*c8dee2aaSAndroid Build Coastguard Worker int SPIRVCodeGenerator::findUniformFieldIndex(const Variable& var) const {
3297*c8dee2aaSAndroid Build Coastguard Worker     int* fieldIndex = fTopLevelUniformMap.find(&var);
3298*c8dee2aaSAndroid Build Coastguard Worker     return fieldIndex ? *fieldIndex : -1;
3299*c8dee2aaSAndroid Build Coastguard Worker }
3300*c8dee2aaSAndroid Build Coastguard Worker 
getLValue(const Expression & expr,OutputStream & out)3301*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SPIRVCodeGenerator::LValue> SPIRVCodeGenerator::getLValue(const Expression& expr,
3302*c8dee2aaSAndroid Build Coastguard Worker                                                                           OutputStream& out) {
3303*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = expr.type();
3304*c8dee2aaSAndroid Build Coastguard Worker     Precision precision = type.highPrecision() ? Precision::kDefault : Precision::kRelaxed;
3305*c8dee2aaSAndroid Build Coastguard Worker     switch (expr.kind()) {
3306*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kVariableReference: {
3307*c8dee2aaSAndroid Build Coastguard Worker             const Variable& var = *expr.as<VariableReference>().variable();
3308*c8dee2aaSAndroid Build Coastguard Worker             int uniformIdx = this->findUniformFieldIndex(var);
3309*c8dee2aaSAndroid Build Coastguard Worker             if (uniformIdx >= 0) {
3310*c8dee2aaSAndroid Build Coastguard Worker                 // Access uniforms via an AccessChain into the uniform-buffer struct.
3311*c8dee2aaSAndroid Build Coastguard Worker                 SpvId memberId = this->nextId(nullptr);
3312*c8dee2aaSAndroid Build Coastguard Worker                 SpvId typeId = this->getPointerType(type, StorageClass::kUniform);
3313*c8dee2aaSAndroid Build Coastguard Worker                 SpvId uniformIdxId = this->writeLiteral((double)uniformIdx, *fContext.fTypes.fInt);
3314*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(SpvOpAccessChain, typeId, memberId, fUniformBufferId,
3315*c8dee2aaSAndroid Build Coastguard Worker                                        uniformIdxId, out);
3316*c8dee2aaSAndroid Build Coastguard Worker                 return std::make_unique<PointerLValue>(
3317*c8dee2aaSAndroid Build Coastguard Worker                         *this,
3318*c8dee2aaSAndroid Build Coastguard Worker                         memberId,
3319*c8dee2aaSAndroid Build Coastguard Worker                         /*isMemoryObjectPointer=*/true,
3320*c8dee2aaSAndroid Build Coastguard Worker                         this->getType(type, kDefaultTypeLayout, this->memoryLayoutForVariable(var)),
3321*c8dee2aaSAndroid Build Coastguard Worker                         precision,
3322*c8dee2aaSAndroid Build Coastguard Worker                         StorageClass::kUniform);
3323*c8dee2aaSAndroid Build Coastguard Worker             }
3324*c8dee2aaSAndroid Build Coastguard Worker 
3325*c8dee2aaSAndroid Build Coastguard Worker             SpvId* entry = fVariableMap.find(&var);
3326*c8dee2aaSAndroid Build Coastguard Worker             SkASSERTF(entry, "%s", expr.description().c_str());
3327*c8dee2aaSAndroid Build Coastguard Worker 
3328*c8dee2aaSAndroid Build Coastguard Worker             if (var.layout().fBuiltin == SK_SAMPLEMASKIN_BUILTIN ||
3329*c8dee2aaSAndroid Build Coastguard Worker                 var.layout().fBuiltin == SK_SAMPLEMASK_BUILTIN) {
3330*c8dee2aaSAndroid Build Coastguard Worker                 // Access sk_SampleMask and sk_SampleMaskIn via an array access, since Vulkan
3331*c8dee2aaSAndroid Build Coastguard Worker                 // represents sample masks as an array of uints.
3332*c8dee2aaSAndroid Build Coastguard Worker                 StorageClass storageClass =
3333*c8dee2aaSAndroid Build Coastguard Worker                         get_storage_class_for_global_variable(var, StorageClass::kPrivate);
3334*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(storageClass != StorageClass::kPrivate);
3335*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(type.matches(*fContext.fTypes.fUInt));
3336*c8dee2aaSAndroid Build Coastguard Worker 
3337*c8dee2aaSAndroid Build Coastguard Worker                 SpvId accessId = this->nextId(nullptr);
3338*c8dee2aaSAndroid Build Coastguard Worker                 SpvId typeId = this->getPointerType(type, storageClass);
3339*c8dee2aaSAndroid Build Coastguard Worker                 SpvId indexId = this->writeLiteral(0.0, *fContext.fTypes.fInt);
3340*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(SpvOpAccessChain, typeId, accessId, *entry, indexId, out);
3341*c8dee2aaSAndroid Build Coastguard Worker                 return std::make_unique<PointerLValue>(*this,
3342*c8dee2aaSAndroid Build Coastguard Worker                                                        accessId,
3343*c8dee2aaSAndroid Build Coastguard Worker                                                        /*isMemoryObjectPointer=*/true,
3344*c8dee2aaSAndroid Build Coastguard Worker                                                        this->getType(type),
3345*c8dee2aaSAndroid Build Coastguard Worker                                                        precision,
3346*c8dee2aaSAndroid Build Coastguard Worker                                                        storageClass);
3347*c8dee2aaSAndroid Build Coastguard Worker             }
3348*c8dee2aaSAndroid Build Coastguard Worker 
3349*c8dee2aaSAndroid Build Coastguard Worker             SpvId typeId = this->getType(type, var.layout(), this->memoryLayoutForVariable(var));
3350*c8dee2aaSAndroid Build Coastguard Worker             return std::make_unique<PointerLValue>(*this, *entry,
3351*c8dee2aaSAndroid Build Coastguard Worker                                                    /*isMemoryObjectPointer=*/true,
3352*c8dee2aaSAndroid Build Coastguard Worker                                                    typeId, precision, this->getStorageClass(expr));
3353*c8dee2aaSAndroid Build Coastguard Worker         }
3354*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kIndex: // fall through
3355*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kFieldAccess: {
3356*c8dee2aaSAndroid Build Coastguard Worker             TArray<SpvId> chain = this->getAccessChain(expr, out);
3357*c8dee2aaSAndroid Build Coastguard Worker             SpvId member = this->nextId(nullptr);
3358*c8dee2aaSAndroid Build Coastguard Worker             StorageClass storageClass = this->getStorageClass(expr);
3359*c8dee2aaSAndroid Build Coastguard Worker             this->writeOpCode(SpvOpAccessChain, (SpvId) (3 + chain.size()), out);
3360*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(this->getPointerType(type, storageClass), out);
3361*c8dee2aaSAndroid Build Coastguard Worker             this->writeWord(member, out);
3362*c8dee2aaSAndroid Build Coastguard Worker             for (SpvId idx : chain) {
3363*c8dee2aaSAndroid Build Coastguard Worker                 this->writeWord(idx, out);
3364*c8dee2aaSAndroid Build Coastguard Worker             }
3365*c8dee2aaSAndroid Build Coastguard Worker             return std::make_unique<PointerLValue>(
3366*c8dee2aaSAndroid Build Coastguard Worker                     *this,
3367*c8dee2aaSAndroid Build Coastguard Worker                     member,
3368*c8dee2aaSAndroid Build Coastguard Worker                     /*isMemoryObjectPointer=*/false,
3369*c8dee2aaSAndroid Build Coastguard Worker                     this->getType(type,
3370*c8dee2aaSAndroid Build Coastguard Worker                                   kDefaultTypeLayout,
3371*c8dee2aaSAndroid Build Coastguard Worker                                   this->memoryLayoutForStorageClass(storageClass)),
3372*c8dee2aaSAndroid Build Coastguard Worker                     precision,
3373*c8dee2aaSAndroid Build Coastguard Worker                     storageClass);
3374*c8dee2aaSAndroid Build Coastguard Worker         }
3375*c8dee2aaSAndroid Build Coastguard Worker         case Expression::Kind::kSwizzle: {
3376*c8dee2aaSAndroid Build Coastguard Worker             const Swizzle& swizzle = expr.as<Swizzle>();
3377*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<LValue> lvalue = this->getLValue(*swizzle.base(), out);
3378*c8dee2aaSAndroid Build Coastguard Worker             if (lvalue->applySwizzle(swizzle.components(), type)) {
3379*c8dee2aaSAndroid Build Coastguard Worker                 return lvalue;
3380*c8dee2aaSAndroid Build Coastguard Worker             }
3381*c8dee2aaSAndroid Build Coastguard Worker             SpvId base = lvalue->getPointer();
3382*c8dee2aaSAndroid Build Coastguard Worker             if (base == NA) {
3383*c8dee2aaSAndroid Build Coastguard Worker                 fContext.fErrors->error(swizzle.fPosition,
3384*c8dee2aaSAndroid Build Coastguard Worker                         "unable to retrieve lvalue from swizzle");
3385*c8dee2aaSAndroid Build Coastguard Worker             }
3386*c8dee2aaSAndroid Build Coastguard Worker             StorageClass storageClass = this->getStorageClass(*swizzle.base());
3387*c8dee2aaSAndroid Build Coastguard Worker             if (swizzle.components().size() == 1) {
3388*c8dee2aaSAndroid Build Coastguard Worker                 SpvId member = this->nextId(nullptr);
3389*c8dee2aaSAndroid Build Coastguard Worker                 SpvId typeId = this->getPointerType(type, storageClass);
3390*c8dee2aaSAndroid Build Coastguard Worker                 SpvId indexId = this->writeLiteral(swizzle.components()[0], *fContext.fTypes.fInt);
3391*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(SpvOpAccessChain, typeId, member, base, indexId, out);
3392*c8dee2aaSAndroid Build Coastguard Worker                 return std::make_unique<PointerLValue>(*this, member,
3393*c8dee2aaSAndroid Build Coastguard Worker                                                        /*isMemoryObjectPointer=*/false,
3394*c8dee2aaSAndroid Build Coastguard Worker                                                        this->getType(type),
3395*c8dee2aaSAndroid Build Coastguard Worker                                                        precision, storageClass);
3396*c8dee2aaSAndroid Build Coastguard Worker             } else {
3397*c8dee2aaSAndroid Build Coastguard Worker                 return std::make_unique<SwizzleLValue>(*this, base, swizzle.components(),
3398*c8dee2aaSAndroid Build Coastguard Worker                                                        swizzle.base()->type(), type, storageClass);
3399*c8dee2aaSAndroid Build Coastguard Worker             }
3400*c8dee2aaSAndroid Build Coastguard Worker         }
3401*c8dee2aaSAndroid Build Coastguard Worker         default: {
3402*c8dee2aaSAndroid Build Coastguard Worker             // expr isn't actually an lvalue, create a placeholder variable for it. This case
3403*c8dee2aaSAndroid Build Coastguard Worker             // happens due to the need to store values in temporary variables during function
3404*c8dee2aaSAndroid Build Coastguard Worker             // calls (see comments in getFunctionParameterType); erroneous uses of rvalues as
3405*c8dee2aaSAndroid Build Coastguard Worker             // lvalues should have been caught before code generation.
3406*c8dee2aaSAndroid Build Coastguard Worker             //
3407*c8dee2aaSAndroid Build Coastguard Worker             // This is with the exception of opaque handle types (textures/samplers) which are
3408*c8dee2aaSAndroid Build Coastguard Worker             // always defined as UniformConstant pointers and don't need to be explicitly stored
3409*c8dee2aaSAndroid Build Coastguard Worker             // into a temporary (which is handled explicitly in writeFunctionCallArgument).
3410*c8dee2aaSAndroid Build Coastguard Worker             SpvId result = this->nextId(nullptr);
3411*c8dee2aaSAndroid Build Coastguard Worker             SpvId pointerType = this->getPointerType(type, StorageClass::kFunction);
3412*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpVariable, pointerType, result, SpvStorageClassFunction,
3413*c8dee2aaSAndroid Build Coastguard Worker                                    fVariableBuffer);
3414*c8dee2aaSAndroid Build Coastguard Worker             this->writeOpStore(StorageClass::kFunction, result, this->writeExpression(expr, out),
3415*c8dee2aaSAndroid Build Coastguard Worker                                out);
3416*c8dee2aaSAndroid Build Coastguard Worker             return std::make_unique<PointerLValue>(*this, result, /*isMemoryObjectPointer=*/true,
3417*c8dee2aaSAndroid Build Coastguard Worker                                                    this->getType(type), precision,
3418*c8dee2aaSAndroid Build Coastguard Worker                                                    StorageClass::kFunction);
3419*c8dee2aaSAndroid Build Coastguard Worker         }
3420*c8dee2aaSAndroid Build Coastguard Worker     }
3421*c8dee2aaSAndroid Build Coastguard Worker }
3422*c8dee2aaSAndroid Build Coastguard Worker 
identifier(std::string_view name)3423*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<Expression> SPIRVCodeGenerator::identifier(std::string_view name) {
3424*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Expression> expr =
3425*c8dee2aaSAndroid Build Coastguard Worker             fProgram.fSymbols->instantiateSymbolRef(fContext, name, Position());
3426*c8dee2aaSAndroid Build Coastguard Worker     return expr ? std::move(expr)
3427*c8dee2aaSAndroid Build Coastguard Worker                 : Poison::Make(Position(), fContext);
3428*c8dee2aaSAndroid Build Coastguard Worker }
3429*c8dee2aaSAndroid Build Coastguard Worker 
writeVariableReference(const VariableReference & ref,OutputStream & out)3430*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeVariableReference(const VariableReference& ref, OutputStream& out) {
3431*c8dee2aaSAndroid Build Coastguard Worker     const Variable* variable = ref.variable();
3432*c8dee2aaSAndroid Build Coastguard Worker     switch (variable->layout().fBuiltin) {
3433*c8dee2aaSAndroid Build Coastguard Worker         case DEVICE_FRAGCOORDS_BUILTIN: {
3434*c8dee2aaSAndroid Build Coastguard Worker             // Down below, we rewrite raw references to sk_FragCoord with expressions that reference
3435*c8dee2aaSAndroid Build Coastguard Worker             // DEVICE_FRAGCOORDS_BUILTIN. This is a fake variable that means we need to directly
3436*c8dee2aaSAndroid Build Coastguard Worker             // access the fragcoord; do so now.
3437*c8dee2aaSAndroid Build Coastguard Worker             return this->getLValue(*this->identifier("sk_FragCoord"), out)->load(out);
3438*c8dee2aaSAndroid Build Coastguard Worker         }
3439*c8dee2aaSAndroid Build Coastguard Worker         case DEVICE_CLOCKWISE_BUILTIN: {
3440*c8dee2aaSAndroid Build Coastguard Worker             // Down below, we rewrite raw references to sk_Clockwise with expressions that reference
3441*c8dee2aaSAndroid Build Coastguard Worker             // DEVICE_CLOCKWISE_BUILTIN. This is a fake variable that means we need to directly
3442*c8dee2aaSAndroid Build Coastguard Worker             // access front facing; do so now.
3443*c8dee2aaSAndroid Build Coastguard Worker             return this->getLValue(*this->identifier("sk_Clockwise"), out)->load(out);
3444*c8dee2aaSAndroid Build Coastguard Worker         }
3445*c8dee2aaSAndroid Build Coastguard Worker         case SK_SECONDARYFRAGCOLOR_BUILTIN: {
3446*c8dee2aaSAndroid Build Coastguard Worker             if (fCaps.fDualSourceBlendingSupport) {
3447*c8dee2aaSAndroid Build Coastguard Worker                 return this->getLValue(*this->identifier("sk_SecondaryFragColor"), out)->load(out);
3448*c8dee2aaSAndroid Build Coastguard Worker             } else {
3449*c8dee2aaSAndroid Build Coastguard Worker                 fContext.fErrors->error(ref.position(), "'sk_SecondaryFragColor' not supported");
3450*c8dee2aaSAndroid Build Coastguard Worker                 return NA;
3451*c8dee2aaSAndroid Build Coastguard Worker             }
3452*c8dee2aaSAndroid Build Coastguard Worker         }
3453*c8dee2aaSAndroid Build Coastguard Worker         case SK_FRAGCOORD_BUILTIN: {
3454*c8dee2aaSAndroid Build Coastguard Worker             if (fProgram.fConfig->fSettings.fForceNoRTFlip) {
3455*c8dee2aaSAndroid Build Coastguard Worker                 return this->getLValue(*this->identifier("sk_FragCoord"), out)->load(out);
3456*c8dee2aaSAndroid Build Coastguard Worker             }
3457*c8dee2aaSAndroid Build Coastguard Worker 
3458*c8dee2aaSAndroid Build Coastguard Worker             // Handle inserting use of uniform to flip y when referencing sk_FragCoord.
3459*c8dee2aaSAndroid Build Coastguard Worker             this->addRTFlipUniform(ref.fPosition);
3460*c8dee2aaSAndroid Build Coastguard Worker             // Use sk_RTAdjust to compute the flipped coordinate
3461*c8dee2aaSAndroid Build Coastguard Worker             // Use a uniform to flip the Y coordinate. The new expression will be written in
3462*c8dee2aaSAndroid Build Coastguard Worker             // terms of $device_FragCoords, which is a fake variable that means "access the
3463*c8dee2aaSAndroid Build Coastguard Worker             // underlying fragcoords directly without flipping it".
3464*c8dee2aaSAndroid Build Coastguard Worker             static constexpr char DEVICE_COORDS_NAME[] = "$device_FragCoords";
3465*c8dee2aaSAndroid Build Coastguard Worker             if (!fProgram.fSymbols->find(DEVICE_COORDS_NAME)) {
3466*c8dee2aaSAndroid Build Coastguard Worker                 AutoAttachPoolToThread attach(fProgram.fPool.get());
3467*c8dee2aaSAndroid Build Coastguard Worker                 Layout layout;
3468*c8dee2aaSAndroid Build Coastguard Worker                 layout.fBuiltin = DEVICE_FRAGCOORDS_BUILTIN;
3469*c8dee2aaSAndroid Build Coastguard Worker                 auto coordsVar = Variable::Make(/*pos=*/Position(),
3470*c8dee2aaSAndroid Build Coastguard Worker                                                 /*modifiersPosition=*/Position(),
3471*c8dee2aaSAndroid Build Coastguard Worker                                                 layout,
3472*c8dee2aaSAndroid Build Coastguard Worker                                                 ModifierFlag::kNone,
3473*c8dee2aaSAndroid Build Coastguard Worker                                                 fContext.fTypes.fFloat4.get(),
3474*c8dee2aaSAndroid Build Coastguard Worker                                                 DEVICE_COORDS_NAME,
3475*c8dee2aaSAndroid Build Coastguard Worker                                                 /*mangledName=*/"",
3476*c8dee2aaSAndroid Build Coastguard Worker                                                 /*builtin=*/true,
3477*c8dee2aaSAndroid Build Coastguard Worker                                                 Variable::Storage::kGlobal);
3478*c8dee2aaSAndroid Build Coastguard Worker                 fProgram.fSymbols->add(fContext, std::move(coordsVar));
3479*c8dee2aaSAndroid Build Coastguard Worker             }
3480*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<Expression> deviceCoord = this->identifier(DEVICE_COORDS_NAME);
3481*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<Expression> rtFlip = this->identifier(SKSL_RTFLIP_NAME);
3482*c8dee2aaSAndroid Build Coastguard Worker             SpvId rtFlipX = this->writeSwizzle(*rtFlip, {SwizzleComponent::X}, out);
3483*c8dee2aaSAndroid Build Coastguard Worker             SpvId rtFlipY = this->writeSwizzle(*rtFlip, {SwizzleComponent::Y}, out);
3484*c8dee2aaSAndroid Build Coastguard Worker             SpvId deviceCoordX  = this->writeSwizzle(*deviceCoord, {SwizzleComponent::X}, out);
3485*c8dee2aaSAndroid Build Coastguard Worker             SpvId deviceCoordY  = this->writeSwizzle(*deviceCoord, {SwizzleComponent::Y}, out);
3486*c8dee2aaSAndroid Build Coastguard Worker             SpvId deviceCoordZW = this->writeSwizzle(*deviceCoord, {SwizzleComponent::Z,
3487*c8dee2aaSAndroid Build Coastguard Worker                                                                     SwizzleComponent::W}, out);
3488*c8dee2aaSAndroid Build Coastguard Worker             // Compute `flippedY = u_RTFlip.y * $device_FragCoords.y`.
3489*c8dee2aaSAndroid Build Coastguard Worker             SpvId flippedY = this->writeBinaryExpression(
3490*c8dee2aaSAndroid Build Coastguard Worker                                      *fContext.fTypes.fFloat, rtFlipY, OperatorKind::STAR,
3491*c8dee2aaSAndroid Build Coastguard Worker                                      *fContext.fTypes.fFloat, deviceCoordY,
3492*c8dee2aaSAndroid Build Coastguard Worker                                      *fContext.fTypes.fFloat, out);
3493*c8dee2aaSAndroid Build Coastguard Worker 
3494*c8dee2aaSAndroid Build Coastguard Worker             // Compute `flippedY = u_RTFlip.x + flippedY`.
3495*c8dee2aaSAndroid Build Coastguard Worker             flippedY = this->writeBinaryExpression(
3496*c8dee2aaSAndroid Build Coastguard Worker                                *fContext.fTypes.fFloat, rtFlipX, OperatorKind::PLUS,
3497*c8dee2aaSAndroid Build Coastguard Worker                                *fContext.fTypes.fFloat, flippedY,
3498*c8dee2aaSAndroid Build Coastguard Worker                                *fContext.fTypes.fFloat, out);
3499*c8dee2aaSAndroid Build Coastguard Worker 
3500*c8dee2aaSAndroid Build Coastguard Worker             // Return `float4(deviceCoord.x, flippedY, deviceCoord.zw)`.
3501*c8dee2aaSAndroid Build Coastguard Worker             return this->writeOpCompositeConstruct(*fContext.fTypes.fFloat4,
3502*c8dee2aaSAndroid Build Coastguard Worker                                                    {deviceCoordX, flippedY, deviceCoordZW},
3503*c8dee2aaSAndroid Build Coastguard Worker                                                    out);
3504*c8dee2aaSAndroid Build Coastguard Worker         }
3505*c8dee2aaSAndroid Build Coastguard Worker         case SK_CLOCKWISE_BUILTIN: {
3506*c8dee2aaSAndroid Build Coastguard Worker             if (fProgram.fConfig->fSettings.fForceNoRTFlip) {
3507*c8dee2aaSAndroid Build Coastguard Worker                 return this->getLValue(*this->identifier("sk_Clockwise"), out)->load(out);
3508*c8dee2aaSAndroid Build Coastguard Worker             }
3509*c8dee2aaSAndroid Build Coastguard Worker 
3510*c8dee2aaSAndroid Build Coastguard Worker             // Apply RTFlip to sk_Clockwise.
3511*c8dee2aaSAndroid Build Coastguard Worker             this->addRTFlipUniform(ref.fPosition);
3512*c8dee2aaSAndroid Build Coastguard Worker             // Use a uniform to flip the Y coordinate. The new expression will be written in
3513*c8dee2aaSAndroid Build Coastguard Worker             // terms of $device_Clockwise, which is a fake variable that means "access the
3514*c8dee2aaSAndroid Build Coastguard Worker             // underlying FrontFacing directly".
3515*c8dee2aaSAndroid Build Coastguard Worker             static constexpr char DEVICE_CLOCKWISE_NAME[] = "$device_Clockwise";
3516*c8dee2aaSAndroid Build Coastguard Worker             if (!fProgram.fSymbols->find(DEVICE_CLOCKWISE_NAME)) {
3517*c8dee2aaSAndroid Build Coastguard Worker                 AutoAttachPoolToThread attach(fProgram.fPool.get());
3518*c8dee2aaSAndroid Build Coastguard Worker                 Layout layout;
3519*c8dee2aaSAndroid Build Coastguard Worker                 layout.fBuiltin = DEVICE_CLOCKWISE_BUILTIN;
3520*c8dee2aaSAndroid Build Coastguard Worker                 auto clockwiseVar = Variable::Make(/*pos=*/Position(),
3521*c8dee2aaSAndroid Build Coastguard Worker                                                    /*modifiersPosition=*/Position(),
3522*c8dee2aaSAndroid Build Coastguard Worker                                                    layout,
3523*c8dee2aaSAndroid Build Coastguard Worker                                                    ModifierFlag::kNone,
3524*c8dee2aaSAndroid Build Coastguard Worker                                                    fContext.fTypes.fBool.get(),
3525*c8dee2aaSAndroid Build Coastguard Worker                                                    DEVICE_CLOCKWISE_NAME,
3526*c8dee2aaSAndroid Build Coastguard Worker                                                    /*mangledName=*/"",
3527*c8dee2aaSAndroid Build Coastguard Worker                                                    /*builtin=*/true,
3528*c8dee2aaSAndroid Build Coastguard Worker                                                    Variable::Storage::kGlobal);
3529*c8dee2aaSAndroid Build Coastguard Worker                 fProgram.fSymbols->add(fContext, std::move(clockwiseVar));
3530*c8dee2aaSAndroid Build Coastguard Worker             }
3531*c8dee2aaSAndroid Build Coastguard Worker             // FrontFacing in Vulkan is defined in terms of a top-down render target. In Skia,
3532*c8dee2aaSAndroid Build Coastguard Worker             // we use the default convention of "counter-clockwise face is front".
3533*c8dee2aaSAndroid Build Coastguard Worker 
3534*c8dee2aaSAndroid Build Coastguard Worker             // Compute `positiveRTFlip = (rtFlip.y > 0)`.
3535*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<Expression> rtFlip = this->identifier(SKSL_RTFLIP_NAME);
3536*c8dee2aaSAndroid Build Coastguard Worker             SpvId rtFlipY = this->writeSwizzle(*rtFlip, {SwizzleComponent::Y}, out);
3537*c8dee2aaSAndroid Build Coastguard Worker             SpvId zero = this->writeLiteral(0.0, *fContext.fTypes.fFloat);
3538*c8dee2aaSAndroid Build Coastguard Worker             SpvId positiveRTFlip = this->writeBinaryExpression(
3539*c8dee2aaSAndroid Build Coastguard Worker                                            *fContext.fTypes.fFloat, rtFlipY, OperatorKind::GT,
3540*c8dee2aaSAndroid Build Coastguard Worker                                            *fContext.fTypes.fFloat, zero,
3541*c8dee2aaSAndroid Build Coastguard Worker                                            *fContext.fTypes.fBool, out);
3542*c8dee2aaSAndroid Build Coastguard Worker 
3543*c8dee2aaSAndroid Build Coastguard Worker             // Compute `positiveRTFlip ^^ $device_Clockwise`.
3544*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<Expression> deviceClockwise = this->identifier(DEVICE_CLOCKWISE_NAME);
3545*c8dee2aaSAndroid Build Coastguard Worker             SpvId deviceClockwiseID = this->writeExpression(*deviceClockwise, out);
3546*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryExpression(
3547*c8dee2aaSAndroid Build Coastguard Worker                            *fContext.fTypes.fBool, positiveRTFlip, OperatorKind::LOGICALXOR,
3548*c8dee2aaSAndroid Build Coastguard Worker                            *fContext.fTypes.fBool, deviceClockwiseID,
3549*c8dee2aaSAndroid Build Coastguard Worker                            *fContext.fTypes.fBool, out);
3550*c8dee2aaSAndroid Build Coastguard Worker         }
3551*c8dee2aaSAndroid Build Coastguard Worker         default: {
3552*c8dee2aaSAndroid Build Coastguard Worker             // Constant-propagate variables that have a known compile-time value.
3553*c8dee2aaSAndroid Build Coastguard Worker             if (const Expression* expr = ConstantFolder::GetConstantValueOrNull(ref)) {
3554*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeExpression(*expr, out);
3555*c8dee2aaSAndroid Build Coastguard Worker             }
3556*c8dee2aaSAndroid Build Coastguard Worker 
3557*c8dee2aaSAndroid Build Coastguard Worker             // A reference to a sampler variable at global scope with synthesized texture/sampler
3558*c8dee2aaSAndroid Build Coastguard Worker             // backing should construct a function-scope combined image-sampler from the synthesized
3559*c8dee2aaSAndroid Build Coastguard Worker             // constituents. This is the case in which a sample intrinsic was invoked.
3560*c8dee2aaSAndroid Build Coastguard Worker             //
3561*c8dee2aaSAndroid Build Coastguard Worker             // Variable references to opaque handles (texture/sampler) that appear as the argument
3562*c8dee2aaSAndroid Build Coastguard Worker             // of a user-defined function call are explicitly handled in writeFunctionCallArgument.
3563*c8dee2aaSAndroid Build Coastguard Worker             if (fUseTextureSamplerPairs && variable->type().isSampler()) {
3564*c8dee2aaSAndroid Build Coastguard Worker                 if (const auto* p = fSynthesizedSamplerMap.find(variable)) {
3565*c8dee2aaSAndroid Build Coastguard Worker                     SpvId* imgPtr = fVariableMap.find((*p)->fTexture.get());
3566*c8dee2aaSAndroid Build Coastguard Worker                     SpvId* samplerPtr = fVariableMap.find((*p)->fSampler.get());
3567*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(imgPtr);
3568*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(samplerPtr);
3569*c8dee2aaSAndroid Build Coastguard Worker 
3570*c8dee2aaSAndroid Build Coastguard Worker                     SpvId img = this->writeOpLoad(this->getType((*p)->fTexture->type()),
3571*c8dee2aaSAndroid Build Coastguard Worker                                                   Precision::kDefault, *imgPtr, out);
3572*c8dee2aaSAndroid Build Coastguard Worker                     SpvId sampler = this->writeOpLoad(this->getType((*p)->fSampler->type()),
3573*c8dee2aaSAndroid Build Coastguard Worker                                                       Precision::kDefault,
3574*c8dee2aaSAndroid Build Coastguard Worker                                                       *samplerPtr,
3575*c8dee2aaSAndroid Build Coastguard Worker                                                       out);
3576*c8dee2aaSAndroid Build Coastguard Worker                     SpvId result = this->nextId(nullptr);
3577*c8dee2aaSAndroid Build Coastguard Worker                     this->writeInstruction(SpvOpSampledImage,
3578*c8dee2aaSAndroid Build Coastguard Worker                                            this->getType(variable->type()),
3579*c8dee2aaSAndroid Build Coastguard Worker                                            result,
3580*c8dee2aaSAndroid Build Coastguard Worker                                            img,
3581*c8dee2aaSAndroid Build Coastguard Worker                                            sampler,
3582*c8dee2aaSAndroid Build Coastguard Worker                                            out);
3583*c8dee2aaSAndroid Build Coastguard Worker                     return result;
3584*c8dee2aaSAndroid Build Coastguard Worker                 }
3585*c8dee2aaSAndroid Build Coastguard Worker                 SkDEBUGFAIL("sampler missing from fSynthesizedSamplerMap");
3586*c8dee2aaSAndroid Build Coastguard Worker             }
3587*c8dee2aaSAndroid Build Coastguard Worker             return this->getLValue(ref, out)->load(out);
3588*c8dee2aaSAndroid Build Coastguard Worker         }
3589*c8dee2aaSAndroid Build Coastguard Worker     }
3590*c8dee2aaSAndroid Build Coastguard Worker }
3591*c8dee2aaSAndroid Build Coastguard Worker 
writeIndexExpression(const IndexExpression & expr,OutputStream & out)3592*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeIndexExpression(const IndexExpression& expr, OutputStream& out) {
3593*c8dee2aaSAndroid Build Coastguard Worker     if (expr.base()->type().isVector()) {
3594*c8dee2aaSAndroid Build Coastguard Worker         SpvId base = this->writeExpression(*expr.base(), out);
3595*c8dee2aaSAndroid Build Coastguard Worker         SpvId index = this->writeExpression(*expr.index(), out);
3596*c8dee2aaSAndroid Build Coastguard Worker         SpvId result = this->nextId(nullptr);
3597*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpVectorExtractDynamic, this->getType(expr.type()), result, base,
3598*c8dee2aaSAndroid Build Coastguard Worker                                index, out);
3599*c8dee2aaSAndroid Build Coastguard Worker         return result;
3600*c8dee2aaSAndroid Build Coastguard Worker     }
3601*c8dee2aaSAndroid Build Coastguard Worker     return getLValue(expr, out)->load(out);
3602*c8dee2aaSAndroid Build Coastguard Worker }
3603*c8dee2aaSAndroid Build Coastguard Worker 
writeFieldAccess(const FieldAccess & f,OutputStream & out)3604*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeFieldAccess(const FieldAccess& f, OutputStream& out) {
3605*c8dee2aaSAndroid Build Coastguard Worker     return getLValue(f, out)->load(out);
3606*c8dee2aaSAndroid Build Coastguard Worker }
3607*c8dee2aaSAndroid Build Coastguard Worker 
writeSwizzle(const Expression & baseExpr,const ComponentArray & components,OutputStream & out)3608*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeSwizzle(const Expression& baseExpr,
3609*c8dee2aaSAndroid Build Coastguard Worker                                        const ComponentArray& components,
3610*c8dee2aaSAndroid Build Coastguard Worker                                        OutputStream& out) {
3611*c8dee2aaSAndroid Build Coastguard Worker     size_t count = components.size();
3612*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = baseExpr.type().componentType().toCompound(fContext, count, /*rows=*/1);
3613*c8dee2aaSAndroid Build Coastguard Worker     SpvId base = this->writeExpression(baseExpr, out);
3614*c8dee2aaSAndroid Build Coastguard Worker     if (count == 1) {
3615*c8dee2aaSAndroid Build Coastguard Worker         return this->writeOpCompositeExtract(type, base, components[0], out);
3616*c8dee2aaSAndroid Build Coastguard Worker     }
3617*c8dee2aaSAndroid Build Coastguard Worker 
3618*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(&type);
3619*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(SpvOpVectorShuffle, 5 + (int32_t) count, out);
3620*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(this->getType(type), out);
3621*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(result, out);
3622*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(base, out);
3623*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(base, out);
3624*c8dee2aaSAndroid Build Coastguard Worker     for (int component : components) {
3625*c8dee2aaSAndroid Build Coastguard Worker         this->writeWord(component, out);
3626*c8dee2aaSAndroid Build Coastguard Worker     }
3627*c8dee2aaSAndroid Build Coastguard Worker     return result;
3628*c8dee2aaSAndroid Build Coastguard Worker }
3629*c8dee2aaSAndroid Build Coastguard Worker 
writeSwizzle(const Swizzle & swizzle,OutputStream & out)3630*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeSwizzle(const Swizzle& swizzle, OutputStream& out) {
3631*c8dee2aaSAndroid Build Coastguard Worker     return this->writeSwizzle(*swizzle.base(), swizzle.components(), out);
3632*c8dee2aaSAndroid Build Coastguard Worker }
3633*c8dee2aaSAndroid Build Coastguard Worker 
writeBinaryOperation(const Type & resultType,const Type & operandType,SpvId lhs,SpvId rhs,bool writeComponentwiseIfMatrix,SpvOp_ ifFloat,SpvOp_ ifInt,SpvOp_ ifUInt,SpvOp_ ifBool,OutputStream & out)3634*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeBinaryOperation(const Type& resultType, const Type& operandType,
3635*c8dee2aaSAndroid Build Coastguard Worker                                                SpvId lhs, SpvId rhs,
3636*c8dee2aaSAndroid Build Coastguard Worker                                                bool writeComponentwiseIfMatrix,
3637*c8dee2aaSAndroid Build Coastguard Worker                                                SpvOp_ ifFloat, SpvOp_ ifInt, SpvOp_ ifUInt,
3638*c8dee2aaSAndroid Build Coastguard Worker                                                SpvOp_ ifBool, OutputStream& out) {
3639*c8dee2aaSAndroid Build Coastguard Worker     SpvOp_ op = pick_by_type(operandType, ifFloat, ifInt, ifUInt, ifBool);
3640*c8dee2aaSAndroid Build Coastguard Worker     if (op == SpvOpUndef) {
3641*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(operandType.fPosition,
3642*c8dee2aaSAndroid Build Coastguard Worker                 "unsupported operand for binary expression: " + operandType.description());
3643*c8dee2aaSAndroid Build Coastguard Worker         return NA;
3644*c8dee2aaSAndroid Build Coastguard Worker     }
3645*c8dee2aaSAndroid Build Coastguard Worker     if (writeComponentwiseIfMatrix && operandType.isMatrix()) {
3646*c8dee2aaSAndroid Build Coastguard Worker         return this->writeComponentwiseMatrixBinary(resultType, lhs, rhs, op, out);
3647*c8dee2aaSAndroid Build Coastguard Worker     }
3648*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(&resultType);
3649*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(op, this->getType(resultType), result, lhs, rhs, out);
3650*c8dee2aaSAndroid Build Coastguard Worker     return result;
3651*c8dee2aaSAndroid Build Coastguard Worker }
3652*c8dee2aaSAndroid Build Coastguard Worker 
writeBinaryOperationComponentwiseIfMatrix(const Type & resultType,const Type & operandType,SpvId lhs,SpvId rhs,SpvOp_ ifFloat,SpvOp_ ifInt,SpvOp_ ifUInt,SpvOp_ ifBool,OutputStream & out)3653*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeBinaryOperationComponentwiseIfMatrix(const Type& resultType,
3654*c8dee2aaSAndroid Build Coastguard Worker                                                                     const Type& operandType,
3655*c8dee2aaSAndroid Build Coastguard Worker                                                                     SpvId lhs, SpvId rhs,
3656*c8dee2aaSAndroid Build Coastguard Worker                                                                     SpvOp_ ifFloat, SpvOp_ ifInt,
3657*c8dee2aaSAndroid Build Coastguard Worker                                                                     SpvOp_ ifUInt, SpvOp_ ifBool,
3658*c8dee2aaSAndroid Build Coastguard Worker                                                                     OutputStream& out) {
3659*c8dee2aaSAndroid Build Coastguard Worker     return this->writeBinaryOperation(resultType, operandType, lhs, rhs,
3660*c8dee2aaSAndroid Build Coastguard Worker                                       /*writeComponentwiseIfMatrix=*/true,
3661*c8dee2aaSAndroid Build Coastguard Worker                                       ifFloat, ifInt, ifUInt, ifBool, out);
3662*c8dee2aaSAndroid Build Coastguard Worker }
3663*c8dee2aaSAndroid Build Coastguard Worker 
writeBinaryOperation(const Type & resultType,const Type & operandType,SpvId lhs,SpvId rhs,SpvOp_ ifFloat,SpvOp_ ifInt,SpvOp_ ifUInt,SpvOp_ ifBool,OutputStream & out)3664*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeBinaryOperation(const Type& resultType, const Type& operandType,
3665*c8dee2aaSAndroid Build Coastguard Worker                                                SpvId lhs, SpvId rhs, SpvOp_ ifFloat, SpvOp_ ifInt,
3666*c8dee2aaSAndroid Build Coastguard Worker                                                SpvOp_ ifUInt, SpvOp_ ifBool, OutputStream& out) {
3667*c8dee2aaSAndroid Build Coastguard Worker     return this->writeBinaryOperation(resultType, operandType, lhs, rhs,
3668*c8dee2aaSAndroid Build Coastguard Worker                                       /*writeComponentwiseIfMatrix=*/false,
3669*c8dee2aaSAndroid Build Coastguard Worker                                       ifFloat, ifInt, ifUInt, ifBool, out);
3670*c8dee2aaSAndroid Build Coastguard Worker }
3671*c8dee2aaSAndroid Build Coastguard Worker 
foldToBool(SpvId id,const Type & operandType,SpvOp op,OutputStream & out)3672*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::foldToBool(SpvId id, const Type& operandType, SpvOp op,
3673*c8dee2aaSAndroid Build Coastguard Worker                                      OutputStream& out) {
3674*c8dee2aaSAndroid Build Coastguard Worker     if (operandType.isVector()) {
3675*c8dee2aaSAndroid Build Coastguard Worker         SpvId result = this->nextId(nullptr);
3676*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(op, this->getType(*fContext.fTypes.fBool), result, id, out);
3677*c8dee2aaSAndroid Build Coastguard Worker         return result;
3678*c8dee2aaSAndroid Build Coastguard Worker     }
3679*c8dee2aaSAndroid Build Coastguard Worker     return id;
3680*c8dee2aaSAndroid Build Coastguard Worker }
3681*c8dee2aaSAndroid Build Coastguard Worker 
writeMatrixComparison(const Type & operandType,SpvId lhs,SpvId rhs,SpvOp_ floatOperator,SpvOp_ intOperator,SpvOp_ vectorMergeOperator,SpvOp_ mergeOperator,OutputStream & out)3682*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeMatrixComparison(const Type& operandType, SpvId lhs, SpvId rhs,
3683*c8dee2aaSAndroid Build Coastguard Worker                                                 SpvOp_ floatOperator, SpvOp_ intOperator,
3684*c8dee2aaSAndroid Build Coastguard Worker                                                 SpvOp_ vectorMergeOperator, SpvOp_ mergeOperator,
3685*c8dee2aaSAndroid Build Coastguard Worker                                                 OutputStream& out) {
3686*c8dee2aaSAndroid Build Coastguard Worker     SpvOp_ compareOp = is_float(operandType) ? floatOperator : intOperator;
3687*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(operandType.isMatrix());
3688*c8dee2aaSAndroid Build Coastguard Worker     const Type& columnType = operandType.componentType().toCompound(fContext,
3689*c8dee2aaSAndroid Build Coastguard Worker                                                                     operandType.rows(),
3690*c8dee2aaSAndroid Build Coastguard Worker                                                                     1);
3691*c8dee2aaSAndroid Build Coastguard Worker     SpvId bvecType = this->getType(fContext.fTypes.fBool->toCompound(fContext,
3692*c8dee2aaSAndroid Build Coastguard Worker                                                                      operandType.rows(),
3693*c8dee2aaSAndroid Build Coastguard Worker                                                                      1));
3694*c8dee2aaSAndroid Build Coastguard Worker     SpvId boolType = this->getType(*fContext.fTypes.fBool);
3695*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = 0;
3696*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < operandType.columns(); i++) {
3697*c8dee2aaSAndroid Build Coastguard Worker         SpvId columnL = this->writeOpCompositeExtract(columnType, lhs, i, out);
3698*c8dee2aaSAndroid Build Coastguard Worker         SpvId columnR = this->writeOpCompositeExtract(columnType, rhs, i, out);
3699*c8dee2aaSAndroid Build Coastguard Worker         SpvId compare = this->nextId(&operandType);
3700*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(compareOp, bvecType, compare, columnL, columnR, out);
3701*c8dee2aaSAndroid Build Coastguard Worker         SpvId merge = this->nextId(nullptr);
3702*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(vectorMergeOperator, boolType, merge, compare, out);
3703*c8dee2aaSAndroid Build Coastguard Worker         if (result != 0) {
3704*c8dee2aaSAndroid Build Coastguard Worker             SpvId next = this->nextId(nullptr);
3705*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(mergeOperator, boolType, next, result, merge, out);
3706*c8dee2aaSAndroid Build Coastguard Worker             result = next;
3707*c8dee2aaSAndroid Build Coastguard Worker         } else {
3708*c8dee2aaSAndroid Build Coastguard Worker             result = merge;
3709*c8dee2aaSAndroid Build Coastguard Worker         }
3710*c8dee2aaSAndroid Build Coastguard Worker     }
3711*c8dee2aaSAndroid Build Coastguard Worker     return result;
3712*c8dee2aaSAndroid Build Coastguard Worker }
3713*c8dee2aaSAndroid Build Coastguard Worker 
writeComponentwiseMatrixUnary(const Type & operandType,SpvId operand,SpvOp_ op,OutputStream & out)3714*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeComponentwiseMatrixUnary(const Type& operandType,
3715*c8dee2aaSAndroid Build Coastguard Worker                                                         SpvId operand,
3716*c8dee2aaSAndroid Build Coastguard Worker                                                         SpvOp_ op,
3717*c8dee2aaSAndroid Build Coastguard Worker                                                         OutputStream& out) {
3718*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(operandType.isMatrix());
3719*c8dee2aaSAndroid Build Coastguard Worker     const Type& columnType = operandType.columnType(fContext);
3720*c8dee2aaSAndroid Build Coastguard Worker     SpvId columnTypeId = this->getType(columnType);
3721*c8dee2aaSAndroid Build Coastguard Worker 
3722*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, SpvId> columns;
3723*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < operandType.columns(); i++) {
3724*c8dee2aaSAndroid Build Coastguard Worker         SpvId srcColumn = this->writeOpCompositeExtract(columnType, operand, i, out);
3725*c8dee2aaSAndroid Build Coastguard Worker         SpvId dstColumn = this->nextId(&operandType);
3726*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(op, columnTypeId, dstColumn, srcColumn, out);
3727*c8dee2aaSAndroid Build Coastguard Worker         columns.push_back(dstColumn);
3728*c8dee2aaSAndroid Build Coastguard Worker     }
3729*c8dee2aaSAndroid Build Coastguard Worker 
3730*c8dee2aaSAndroid Build Coastguard Worker     return this->writeOpCompositeConstruct(operandType, columns, out);
3731*c8dee2aaSAndroid Build Coastguard Worker }
3732*c8dee2aaSAndroid Build Coastguard Worker 
writeComponentwiseMatrixBinary(const Type & operandType,SpvId lhs,SpvId rhs,SpvOp_ op,OutputStream & out)3733*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeComponentwiseMatrixBinary(const Type& operandType, SpvId lhs,
3734*c8dee2aaSAndroid Build Coastguard Worker                                                          SpvId rhs, SpvOp_ op, OutputStream& out) {
3735*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(operandType.isMatrix());
3736*c8dee2aaSAndroid Build Coastguard Worker     const Type& columnType = operandType.columnType(fContext);
3737*c8dee2aaSAndroid Build Coastguard Worker     SpvId columnTypeId = this->getType(columnType);
3738*c8dee2aaSAndroid Build Coastguard Worker 
3739*c8dee2aaSAndroid Build Coastguard Worker     STArray<4, SpvId> columns;
3740*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < operandType.columns(); i++) {
3741*c8dee2aaSAndroid Build Coastguard Worker         SpvId columnL = this->writeOpCompositeExtract(columnType, lhs, i, out);
3742*c8dee2aaSAndroid Build Coastguard Worker         SpvId columnR = this->writeOpCompositeExtract(columnType, rhs, i, out);
3743*c8dee2aaSAndroid Build Coastguard Worker         columns.push_back(this->nextId(&operandType));
3744*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(op, columnTypeId, columns[i], columnL, columnR, out);
3745*c8dee2aaSAndroid Build Coastguard Worker     }
3746*c8dee2aaSAndroid Build Coastguard Worker     return this->writeOpCompositeConstruct(operandType, columns, out);
3747*c8dee2aaSAndroid Build Coastguard Worker }
3748*c8dee2aaSAndroid Build Coastguard Worker 
writeReciprocal(const Type & type,SpvId value,OutputStream & out)3749*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeReciprocal(const Type& type, SpvId value, OutputStream& out) {
3750*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(type.isFloat());
3751*c8dee2aaSAndroid Build Coastguard Worker     SpvId one = this->writeLiteral(1.0, type);
3752*c8dee2aaSAndroid Build Coastguard Worker     SpvId reciprocal = this->nextId(&type);
3753*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpFDiv, this->getType(type), reciprocal, one, value, out);
3754*c8dee2aaSAndroid Build Coastguard Worker     return reciprocal;
3755*c8dee2aaSAndroid Build Coastguard Worker }
3756*c8dee2aaSAndroid Build Coastguard Worker 
splat(const Type & type,SpvId id,OutputStream & out)3757*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::splat(const Type& type, SpvId id, OutputStream& out) {
3758*c8dee2aaSAndroid Build Coastguard Worker     if (type.isScalar()) {
3759*c8dee2aaSAndroid Build Coastguard Worker         // Scalars require no additional work; we can return the passed-in ID as is.
3760*c8dee2aaSAndroid Build Coastguard Worker     } else {
3761*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(type.isVector() || type.isMatrix());
3762*c8dee2aaSAndroid Build Coastguard Worker         bool isMatrix = type.isMatrix();
3763*c8dee2aaSAndroid Build Coastguard Worker 
3764*c8dee2aaSAndroid Build Coastguard Worker         // Splat the input scalar across a vector.
3765*c8dee2aaSAndroid Build Coastguard Worker         int vectorSize = (isMatrix ? type.rows() : type.columns());
3766*c8dee2aaSAndroid Build Coastguard Worker         const Type& vectorType = type.componentType().toCompound(fContext, vectorSize, /*rows=*/1);
3767*c8dee2aaSAndroid Build Coastguard Worker 
3768*c8dee2aaSAndroid Build Coastguard Worker         STArray<4, SpvId> values;
3769*c8dee2aaSAndroid Build Coastguard Worker         values.push_back_n(/*n=*/vectorSize, /*t=*/id);
3770*c8dee2aaSAndroid Build Coastguard Worker         id = this->writeOpCompositeConstruct(vectorType, values, out);
3771*c8dee2aaSAndroid Build Coastguard Worker 
3772*c8dee2aaSAndroid Build Coastguard Worker         if (isMatrix) {
3773*c8dee2aaSAndroid Build Coastguard Worker             // Splat the newly-synthesized vector into a matrix.
3774*c8dee2aaSAndroid Build Coastguard Worker             STArray<4, SpvId> matArguments;
3775*c8dee2aaSAndroid Build Coastguard Worker             matArguments.push_back_n(/*n=*/type.columns(), /*t=*/id);
3776*c8dee2aaSAndroid Build Coastguard Worker             id = this->writeOpCompositeConstruct(type, matArguments, out);
3777*c8dee2aaSAndroid Build Coastguard Worker         }
3778*c8dee2aaSAndroid Build Coastguard Worker     }
3779*c8dee2aaSAndroid Build Coastguard Worker 
3780*c8dee2aaSAndroid Build Coastguard Worker     return id;
3781*c8dee2aaSAndroid Build Coastguard Worker }
3782*c8dee2aaSAndroid Build Coastguard Worker 
types_match(const Type & a,const Type & b)3783*c8dee2aaSAndroid Build Coastguard Worker static bool types_match(const Type& a, const Type& b) {
3784*c8dee2aaSAndroid Build Coastguard Worker     if (a.matches(b)) {
3785*c8dee2aaSAndroid Build Coastguard Worker         return true;
3786*c8dee2aaSAndroid Build Coastguard Worker     }
3787*c8dee2aaSAndroid Build Coastguard Worker     return (a.typeKind() == b.typeKind()) &&
3788*c8dee2aaSAndroid Build Coastguard Worker            (a.isScalar() || a.isVector() || a.isMatrix()) &&
3789*c8dee2aaSAndroid Build Coastguard Worker            (a.columns() == b.columns() && a.rows() == b.rows()) &&
3790*c8dee2aaSAndroid Build Coastguard Worker            a.componentType().numberKind() == b.componentType().numberKind();
3791*c8dee2aaSAndroid Build Coastguard Worker }
3792*c8dee2aaSAndroid Build Coastguard Worker 
writeDecomposedMatrixVectorMultiply(const Type & leftType,SpvId lhs,const Type & rightType,SpvId rhs,const Type & resultType,OutputStream & out)3793*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeDecomposedMatrixVectorMultiply(const Type& leftType,
3794*c8dee2aaSAndroid Build Coastguard Worker                                                               SpvId lhs,
3795*c8dee2aaSAndroid Build Coastguard Worker                                                               const Type& rightType,
3796*c8dee2aaSAndroid Build Coastguard Worker                                                               SpvId rhs,
3797*c8dee2aaSAndroid Build Coastguard Worker                                                               const Type& resultType,
3798*c8dee2aaSAndroid Build Coastguard Worker                                                               OutputStream& out) {
3799*c8dee2aaSAndroid Build Coastguard Worker     SpvId sum = NA;
3800*c8dee2aaSAndroid Build Coastguard Worker     const Type& columnType = leftType.columnType(fContext);
3801*c8dee2aaSAndroid Build Coastguard Worker     const Type& scalarType = rightType.componentType();
3802*c8dee2aaSAndroid Build Coastguard Worker 
3803*c8dee2aaSAndroid Build Coastguard Worker     for (int n = 0; n < leftType.rows(); ++n) {
3804*c8dee2aaSAndroid Build Coastguard Worker         // Extract mat[N] from the matrix.
3805*c8dee2aaSAndroid Build Coastguard Worker         SpvId matN = this->writeOpCompositeExtract(columnType, lhs, n, out);
3806*c8dee2aaSAndroid Build Coastguard Worker 
3807*c8dee2aaSAndroid Build Coastguard Worker         // Extract vec[N] from the vector.
3808*c8dee2aaSAndroid Build Coastguard Worker         SpvId vecN = this->writeOpCompositeExtract(scalarType, rhs, n, out);
3809*c8dee2aaSAndroid Build Coastguard Worker 
3810*c8dee2aaSAndroid Build Coastguard Worker         // Multiply them together.
3811*c8dee2aaSAndroid Build Coastguard Worker         SpvId product = this->writeBinaryExpression(columnType, matN, OperatorKind::STAR,
3812*c8dee2aaSAndroid Build Coastguard Worker                                                     scalarType, vecN,
3813*c8dee2aaSAndroid Build Coastguard Worker                                                     columnType, out);
3814*c8dee2aaSAndroid Build Coastguard Worker 
3815*c8dee2aaSAndroid Build Coastguard Worker         // Sum all the components together.
3816*c8dee2aaSAndroid Build Coastguard Worker         if (sum == NA) {
3817*c8dee2aaSAndroid Build Coastguard Worker             sum = product;
3818*c8dee2aaSAndroid Build Coastguard Worker         } else {
3819*c8dee2aaSAndroid Build Coastguard Worker             sum = this->writeBinaryExpression(columnType, sum, OperatorKind::PLUS,
3820*c8dee2aaSAndroid Build Coastguard Worker                                               columnType, product,
3821*c8dee2aaSAndroid Build Coastguard Worker                                               columnType, out);
3822*c8dee2aaSAndroid Build Coastguard Worker         }
3823*c8dee2aaSAndroid Build Coastguard Worker     }
3824*c8dee2aaSAndroid Build Coastguard Worker 
3825*c8dee2aaSAndroid Build Coastguard Worker     return sum;
3826*c8dee2aaSAndroid Build Coastguard Worker }
3827*c8dee2aaSAndroid Build Coastguard Worker 
writeBinaryExpression(const Type & leftType,SpvId lhs,Operator op,const Type & rightType,SpvId rhs,const Type & resultType,OutputStream & out)3828*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeBinaryExpression(const Type& leftType, SpvId lhs, Operator op,
3829*c8dee2aaSAndroid Build Coastguard Worker                                                 const Type& rightType, SpvId rhs,
3830*c8dee2aaSAndroid Build Coastguard Worker                                                 const Type& resultType, OutputStream& out) {
3831*c8dee2aaSAndroid Build Coastguard Worker     // The comma operator ignores the type of the left-hand side entirely.
3832*c8dee2aaSAndroid Build Coastguard Worker     if (op.kind() == Operator::Kind::COMMA) {
3833*c8dee2aaSAndroid Build Coastguard Worker         return rhs;
3834*c8dee2aaSAndroid Build Coastguard Worker     }
3835*c8dee2aaSAndroid Build Coastguard Worker     // overall type we are operating on: float2, int, uint4...
3836*c8dee2aaSAndroid Build Coastguard Worker     const Type* operandType;
3837*c8dee2aaSAndroid Build Coastguard Worker     if (types_match(leftType, rightType)) {
3838*c8dee2aaSAndroid Build Coastguard Worker         operandType = &leftType;
3839*c8dee2aaSAndroid Build Coastguard Worker     } else {
3840*c8dee2aaSAndroid Build Coastguard Worker         // IR allows mismatched types in expressions (e.g. float2 * float), but they need special
3841*c8dee2aaSAndroid Build Coastguard Worker         // handling in SPIR-V
3842*c8dee2aaSAndroid Build Coastguard Worker         if (leftType.isVector() && rightType.isNumber()) {
3843*c8dee2aaSAndroid Build Coastguard Worker             if (resultType.componentType().isFloat()) {
3844*c8dee2aaSAndroid Build Coastguard Worker                 switch (op.kind()) {
3845*c8dee2aaSAndroid Build Coastguard Worker                     case Operator::Kind::SLASH: {
3846*c8dee2aaSAndroid Build Coastguard Worker                         rhs = this->writeReciprocal(rightType, rhs, out);
3847*c8dee2aaSAndroid Build Coastguard Worker                         [[fallthrough]];
3848*c8dee2aaSAndroid Build Coastguard Worker                     }
3849*c8dee2aaSAndroid Build Coastguard Worker                     case Operator::Kind::STAR: {
3850*c8dee2aaSAndroid Build Coastguard Worker                         SpvId result = this->nextId(&resultType);
3851*c8dee2aaSAndroid Build Coastguard Worker                         this->writeInstruction(SpvOpVectorTimesScalar, this->getType(resultType),
3852*c8dee2aaSAndroid Build Coastguard Worker                                                result, lhs, rhs, out);
3853*c8dee2aaSAndroid Build Coastguard Worker                         return result;
3854*c8dee2aaSAndroid Build Coastguard Worker                     }
3855*c8dee2aaSAndroid Build Coastguard Worker                     default:
3856*c8dee2aaSAndroid Build Coastguard Worker                         break;
3857*c8dee2aaSAndroid Build Coastguard Worker                 }
3858*c8dee2aaSAndroid Build Coastguard Worker             }
3859*c8dee2aaSAndroid Build Coastguard Worker             // Vectorize the right-hand side.
3860*c8dee2aaSAndroid Build Coastguard Worker             STArray<4, SpvId> arguments;
3861*c8dee2aaSAndroid Build Coastguard Worker             arguments.push_back_n(/*n=*/leftType.columns(), /*t=*/rhs);
3862*c8dee2aaSAndroid Build Coastguard Worker             rhs = this->writeOpCompositeConstruct(leftType, arguments, out);
3863*c8dee2aaSAndroid Build Coastguard Worker             operandType = &leftType;
3864*c8dee2aaSAndroid Build Coastguard Worker         } else if (rightType.isVector() && leftType.isNumber()) {
3865*c8dee2aaSAndroid Build Coastguard Worker             if (resultType.componentType().isFloat()) {
3866*c8dee2aaSAndroid Build Coastguard Worker                 if (op.kind() == Operator::Kind::STAR) {
3867*c8dee2aaSAndroid Build Coastguard Worker                     SpvId result = this->nextId(&resultType);
3868*c8dee2aaSAndroid Build Coastguard Worker                     this->writeInstruction(SpvOpVectorTimesScalar, this->getType(resultType),
3869*c8dee2aaSAndroid Build Coastguard Worker                                            result, rhs, lhs, out);
3870*c8dee2aaSAndroid Build Coastguard Worker                     return result;
3871*c8dee2aaSAndroid Build Coastguard Worker                 }
3872*c8dee2aaSAndroid Build Coastguard Worker             }
3873*c8dee2aaSAndroid Build Coastguard Worker             // Vectorize the left-hand side.
3874*c8dee2aaSAndroid Build Coastguard Worker             STArray<4, SpvId> arguments;
3875*c8dee2aaSAndroid Build Coastguard Worker             arguments.push_back_n(/*n=*/rightType.columns(), /*t=*/lhs);
3876*c8dee2aaSAndroid Build Coastguard Worker             lhs = this->writeOpCompositeConstruct(rightType, arguments, out);
3877*c8dee2aaSAndroid Build Coastguard Worker             operandType = &rightType;
3878*c8dee2aaSAndroid Build Coastguard Worker         } else if (leftType.isMatrix()) {
3879*c8dee2aaSAndroid Build Coastguard Worker             if (op.kind() == Operator::Kind::STAR) {
3880*c8dee2aaSAndroid Build Coastguard Worker                 // When the rewriteMatrixVectorMultiply bit is set, we rewrite medium-precision
3881*c8dee2aaSAndroid Build Coastguard Worker                 // matrix * vector multiplication as (mat[0]*vec[0] + ... + mat[N]*vec[N]).
3882*c8dee2aaSAndroid Build Coastguard Worker                 if (fCaps.fRewriteMatrixVectorMultiply &&
3883*c8dee2aaSAndroid Build Coastguard Worker                     rightType.isVector() &&
3884*c8dee2aaSAndroid Build Coastguard Worker                     !resultType.highPrecision()) {
3885*c8dee2aaSAndroid Build Coastguard Worker                     return this->writeDecomposedMatrixVectorMultiply(leftType, lhs, rightType, rhs,
3886*c8dee2aaSAndroid Build Coastguard Worker                                                                      resultType, out);
3887*c8dee2aaSAndroid Build Coastguard Worker                 }
3888*c8dee2aaSAndroid Build Coastguard Worker 
3889*c8dee2aaSAndroid Build Coastguard Worker                 // Matrix-times-vector and matrix-times-scalar have dedicated ops in SPIR-V.
3890*c8dee2aaSAndroid Build Coastguard Worker                 SpvOp_ spvop;
3891*c8dee2aaSAndroid Build Coastguard Worker                 if (rightType.isMatrix()) {
3892*c8dee2aaSAndroid Build Coastguard Worker                     spvop = SpvOpMatrixTimesMatrix;
3893*c8dee2aaSAndroid Build Coastguard Worker                 } else if (rightType.isVector()) {
3894*c8dee2aaSAndroid Build Coastguard Worker                     spvop = SpvOpMatrixTimesVector;
3895*c8dee2aaSAndroid Build Coastguard Worker                 } else {
3896*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(rightType.isScalar());
3897*c8dee2aaSAndroid Build Coastguard Worker                     spvop = SpvOpMatrixTimesScalar;
3898*c8dee2aaSAndroid Build Coastguard Worker                 }
3899*c8dee2aaSAndroid Build Coastguard Worker                 SpvId result = this->nextId(&resultType);
3900*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(spvop, this->getType(resultType), result, lhs, rhs, out);
3901*c8dee2aaSAndroid Build Coastguard Worker                 return result;
3902*c8dee2aaSAndroid Build Coastguard Worker             } else {
3903*c8dee2aaSAndroid Build Coastguard Worker                 // Matrix-op-vector is not supported in GLSL/SkSL for non-multiplication ops; we
3904*c8dee2aaSAndroid Build Coastguard Worker                 // expect to have a scalar here.
3905*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(rightType.isScalar());
3906*c8dee2aaSAndroid Build Coastguard Worker 
3907*c8dee2aaSAndroid Build Coastguard Worker                 // Splat rhs across an entire matrix so we can reuse the matrix-op-matrix path.
3908*c8dee2aaSAndroid Build Coastguard Worker                 SpvId rhsMatrix = this->splat(leftType, rhs, out);
3909*c8dee2aaSAndroid Build Coastguard Worker 
3910*c8dee2aaSAndroid Build Coastguard Worker                 // Perform this operation as matrix-op-matrix.
3911*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeBinaryExpression(leftType, lhs, op, leftType, rhsMatrix,
3912*c8dee2aaSAndroid Build Coastguard Worker                                                    resultType, out);
3913*c8dee2aaSAndroid Build Coastguard Worker             }
3914*c8dee2aaSAndroid Build Coastguard Worker         } else if (rightType.isMatrix()) {
3915*c8dee2aaSAndroid Build Coastguard Worker             if (op.kind() == Operator::Kind::STAR) {
3916*c8dee2aaSAndroid Build Coastguard Worker                 // Matrix-times-vector and matrix-times-scalar have dedicated ops in SPIR-V.
3917*c8dee2aaSAndroid Build Coastguard Worker                 SpvId result = this->nextId(&resultType);
3918*c8dee2aaSAndroid Build Coastguard Worker                 if (leftType.isVector()) {
3919*c8dee2aaSAndroid Build Coastguard Worker                     this->writeInstruction(SpvOpVectorTimesMatrix, this->getType(resultType),
3920*c8dee2aaSAndroid Build Coastguard Worker                                            result, lhs, rhs, out);
3921*c8dee2aaSAndroid Build Coastguard Worker                 } else {
3922*c8dee2aaSAndroid Build Coastguard Worker                     SkASSERT(leftType.isScalar());
3923*c8dee2aaSAndroid Build Coastguard Worker                     this->writeInstruction(SpvOpMatrixTimesScalar, this->getType(resultType),
3924*c8dee2aaSAndroid Build Coastguard Worker                                            result, rhs, lhs, out);
3925*c8dee2aaSAndroid Build Coastguard Worker                 }
3926*c8dee2aaSAndroid Build Coastguard Worker                 return result;
3927*c8dee2aaSAndroid Build Coastguard Worker             } else {
3928*c8dee2aaSAndroid Build Coastguard Worker                 // Vector-op-matrix is not supported in GLSL/SkSL for non-multiplication ops; we
3929*c8dee2aaSAndroid Build Coastguard Worker                 // expect to have a scalar here.
3930*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(leftType.isScalar());
3931*c8dee2aaSAndroid Build Coastguard Worker 
3932*c8dee2aaSAndroid Build Coastguard Worker                 // Splat lhs across an entire matrix so we can reuse the matrix-op-matrix path.
3933*c8dee2aaSAndroid Build Coastguard Worker                 SpvId lhsMatrix = this->splat(rightType, lhs, out);
3934*c8dee2aaSAndroid Build Coastguard Worker 
3935*c8dee2aaSAndroid Build Coastguard Worker                 // Perform this operation as matrix-op-matrix.
3936*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeBinaryExpression(rightType, lhsMatrix, op, rightType, rhs,
3937*c8dee2aaSAndroid Build Coastguard Worker                                                    resultType, out);
3938*c8dee2aaSAndroid Build Coastguard Worker             }
3939*c8dee2aaSAndroid Build Coastguard Worker         } else {
3940*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(leftType.fPosition, "unsupported mixed-type expression");
3941*c8dee2aaSAndroid Build Coastguard Worker             return NA;
3942*c8dee2aaSAndroid Build Coastguard Worker         }
3943*c8dee2aaSAndroid Build Coastguard Worker     }
3944*c8dee2aaSAndroid Build Coastguard Worker 
3945*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
3946*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::EQEQ: {
3947*c8dee2aaSAndroid Build Coastguard Worker             if (operandType->isMatrix()) {
3948*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeMatrixComparison(*operandType, lhs, rhs, SpvOpFOrdEqual,
3949*c8dee2aaSAndroid Build Coastguard Worker                                                    SpvOpIEqual, SpvOpAll, SpvOpLogicalAnd, out);
3950*c8dee2aaSAndroid Build Coastguard Worker             }
3951*c8dee2aaSAndroid Build Coastguard Worker             if (operandType->isStruct()) {
3952*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeStructComparison(*operandType, lhs, op, rhs, out);
3953*c8dee2aaSAndroid Build Coastguard Worker             }
3954*c8dee2aaSAndroid Build Coastguard Worker             if (operandType->isArray()) {
3955*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeArrayComparison(*operandType, lhs, op, rhs, out);
3956*c8dee2aaSAndroid Build Coastguard Worker             }
3957*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resultType.isBoolean());
3958*c8dee2aaSAndroid Build Coastguard Worker             const Type* tmpType;
3959*c8dee2aaSAndroid Build Coastguard Worker             if (operandType->isVector()) {
3960*c8dee2aaSAndroid Build Coastguard Worker                 tmpType = &fContext.fTypes.fBool->toCompound(fContext,
3961*c8dee2aaSAndroid Build Coastguard Worker                                                              operandType->columns(),
3962*c8dee2aaSAndroid Build Coastguard Worker                                                              operandType->rows());
3963*c8dee2aaSAndroid Build Coastguard Worker             } else {
3964*c8dee2aaSAndroid Build Coastguard Worker                 tmpType = &resultType;
3965*c8dee2aaSAndroid Build Coastguard Worker             }
3966*c8dee2aaSAndroid Build Coastguard Worker             if (lhs == rhs) {
3967*c8dee2aaSAndroid Build Coastguard Worker                 // This ignores the effects of NaN.
3968*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeOpConstantTrue(*fContext.fTypes.fBool);
3969*c8dee2aaSAndroid Build Coastguard Worker             }
3970*c8dee2aaSAndroid Build Coastguard Worker             return this->foldToBool(this->writeBinaryOperation(*tmpType, *operandType, lhs, rhs,
3971*c8dee2aaSAndroid Build Coastguard Worker                                                                SpvOpFOrdEqual, SpvOpIEqual,
3972*c8dee2aaSAndroid Build Coastguard Worker                                                                SpvOpIEqual, SpvOpLogicalEqual, out),
3973*c8dee2aaSAndroid Build Coastguard Worker                                     *operandType, SpvOpAll, out);
3974*c8dee2aaSAndroid Build Coastguard Worker         }
3975*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::NEQ:
3976*c8dee2aaSAndroid Build Coastguard Worker             if (operandType->isMatrix()) {
3977*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeMatrixComparison(*operandType, lhs, rhs, SpvOpFUnordNotEqual,
3978*c8dee2aaSAndroid Build Coastguard Worker                                                    SpvOpINotEqual, SpvOpAny, SpvOpLogicalOr, out);
3979*c8dee2aaSAndroid Build Coastguard Worker             }
3980*c8dee2aaSAndroid Build Coastguard Worker             if (operandType->isStruct()) {
3981*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeStructComparison(*operandType, lhs, op, rhs, out);
3982*c8dee2aaSAndroid Build Coastguard Worker             }
3983*c8dee2aaSAndroid Build Coastguard Worker             if (operandType->isArray()) {
3984*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeArrayComparison(*operandType, lhs, op, rhs, out);
3985*c8dee2aaSAndroid Build Coastguard Worker             }
3986*c8dee2aaSAndroid Build Coastguard Worker             [[fallthrough]];
3987*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::LOGICALXOR:
3988*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resultType.isBoolean());
3989*c8dee2aaSAndroid Build Coastguard Worker             const Type* tmpType;
3990*c8dee2aaSAndroid Build Coastguard Worker             if (operandType->isVector()) {
3991*c8dee2aaSAndroid Build Coastguard Worker                 tmpType = &fContext.fTypes.fBool->toCompound(fContext,
3992*c8dee2aaSAndroid Build Coastguard Worker                                                              operandType->columns(),
3993*c8dee2aaSAndroid Build Coastguard Worker                                                              operandType->rows());
3994*c8dee2aaSAndroid Build Coastguard Worker             } else {
3995*c8dee2aaSAndroid Build Coastguard Worker                 tmpType = &resultType;
3996*c8dee2aaSAndroid Build Coastguard Worker             }
3997*c8dee2aaSAndroid Build Coastguard Worker             if (lhs == rhs) {
3998*c8dee2aaSAndroid Build Coastguard Worker                 // This ignores the effects of NaN.
3999*c8dee2aaSAndroid Build Coastguard Worker                 return this->writeOpConstantFalse(*fContext.fTypes.fBool);
4000*c8dee2aaSAndroid Build Coastguard Worker             }
4001*c8dee2aaSAndroid Build Coastguard Worker             return this->foldToBool(this->writeBinaryOperation(*tmpType, *operandType, lhs, rhs,
4002*c8dee2aaSAndroid Build Coastguard Worker                                                                SpvOpFUnordNotEqual, SpvOpINotEqual,
4003*c8dee2aaSAndroid Build Coastguard Worker                                                                SpvOpINotEqual, SpvOpLogicalNotEqual,
4004*c8dee2aaSAndroid Build Coastguard Worker                                                                out),
4005*c8dee2aaSAndroid Build Coastguard Worker                                     *operandType, SpvOpAny, out);
4006*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::GT:
4007*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resultType.isBoolean());
4008*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
4009*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpFOrdGreaterThan, SpvOpSGreaterThan,
4010*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpUGreaterThan, SpvOpUndef, out);
4011*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::LT:
4012*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resultType.isBoolean());
4013*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFOrdLessThan,
4014*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpSLessThan, SpvOpULessThan, SpvOpUndef, out);
4015*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::GTEQ:
4016*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resultType.isBoolean());
4017*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
4018*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpFOrdGreaterThanEqual, SpvOpSGreaterThanEqual,
4019*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpUGreaterThanEqual, SpvOpUndef, out);
4020*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::LTEQ:
4021*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(resultType.isBoolean());
4022*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs,
4023*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpFOrdLessThanEqual, SpvOpSLessThanEqual,
4024*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpULessThanEqual, SpvOpUndef, out);
4025*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::PLUS:
4026*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperationComponentwiseIfMatrix(resultType, *operandType,
4027*c8dee2aaSAndroid Build Coastguard Worker                                                                    lhs, rhs, SpvOpFAdd, SpvOpIAdd,
4028*c8dee2aaSAndroid Build Coastguard Worker                                                                    SpvOpIAdd, SpvOpUndef, out);
4029*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::MINUS:
4030*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperationComponentwiseIfMatrix(resultType, *operandType,
4031*c8dee2aaSAndroid Build Coastguard Worker                                                                    lhs, rhs, SpvOpFSub, SpvOpISub,
4032*c8dee2aaSAndroid Build Coastguard Worker                                                                    SpvOpISub, SpvOpUndef, out);
4033*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::STAR:
4034*c8dee2aaSAndroid Build Coastguard Worker             if (leftType.isMatrix() && rightType.isMatrix()) {
4035*c8dee2aaSAndroid Build Coastguard Worker                 // matrix multiply
4036*c8dee2aaSAndroid Build Coastguard Worker                 SpvId result = this->nextId(&resultType);
4037*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(SpvOpMatrixTimesMatrix, this->getType(resultType), result,
4038*c8dee2aaSAndroid Build Coastguard Worker                                        lhs, rhs, out);
4039*c8dee2aaSAndroid Build Coastguard Worker                 return result;
4040*c8dee2aaSAndroid Build Coastguard Worker             }
4041*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFMul,
4042*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpIMul, SpvOpIMul, SpvOpUndef, out);
4043*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::SLASH:
4044*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperationComponentwiseIfMatrix(resultType, *operandType,
4045*c8dee2aaSAndroid Build Coastguard Worker                                                                    lhs, rhs, SpvOpFDiv, SpvOpSDiv,
4046*c8dee2aaSAndroid Build Coastguard Worker                                                                    SpvOpUDiv, SpvOpUndef, out);
4047*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::PERCENT:
4048*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpFMod,
4049*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpSMod, SpvOpUMod, SpvOpUndef, out);
4050*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::SHL:
4051*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpUndef,
4052*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpShiftLeftLogical, SpvOpShiftLeftLogical,
4053*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpUndef, out);
4054*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::SHR:
4055*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpUndef,
4056*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpShiftRightArithmetic, SpvOpShiftRightLogical,
4057*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpUndef, out);
4058*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::BITWISEAND:
4059*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpUndef,
4060*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpBitwiseAnd, SpvOpBitwiseAnd, SpvOpUndef, out);
4061*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::BITWISEOR:
4062*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpUndef,
4063*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpBitwiseOr, SpvOpBitwiseOr, SpvOpUndef, out);
4064*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::BITWISEXOR:
4065*c8dee2aaSAndroid Build Coastguard Worker             return this->writeBinaryOperation(resultType, *operandType, lhs, rhs, SpvOpUndef,
4066*c8dee2aaSAndroid Build Coastguard Worker                                               SpvOpBitwiseXor, SpvOpBitwiseXor, SpvOpUndef, out);
4067*c8dee2aaSAndroid Build Coastguard Worker         default:
4068*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(Position(), "unsupported token");
4069*c8dee2aaSAndroid Build Coastguard Worker             return NA;
4070*c8dee2aaSAndroid Build Coastguard Worker     }
4071*c8dee2aaSAndroid Build Coastguard Worker }
4072*c8dee2aaSAndroid Build Coastguard Worker 
writeArrayComparison(const Type & arrayType,SpvId lhs,Operator op,SpvId rhs,OutputStream & out)4073*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeArrayComparison(const Type& arrayType, SpvId lhs, Operator op,
4074*c8dee2aaSAndroid Build Coastguard Worker                                                SpvId rhs, OutputStream& out) {
4075*c8dee2aaSAndroid Build Coastguard Worker     // The inputs must be arrays, and the op must be == or !=.
4076*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(op.kind() == Operator::Kind::EQEQ || op.kind() == Operator::Kind::NEQ);
4077*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(arrayType.isArray());
4078*c8dee2aaSAndroid Build Coastguard Worker     const Type& componentType = arrayType.componentType();
4079*c8dee2aaSAndroid Build Coastguard Worker     const int arraySize = arrayType.columns();
4080*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(arraySize > 0);
4081*c8dee2aaSAndroid Build Coastguard Worker 
4082*c8dee2aaSAndroid Build Coastguard Worker     // Synthesize equality checks for each item in the array.
4083*c8dee2aaSAndroid Build Coastguard Worker     const Type& boolType = *fContext.fTypes.fBool;
4084*c8dee2aaSAndroid Build Coastguard Worker     SpvId allComparisons = NA;
4085*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < arraySize; ++index) {
4086*c8dee2aaSAndroid Build Coastguard Worker         // Get the left and right item in the array.
4087*c8dee2aaSAndroid Build Coastguard Worker         SpvId itemL = this->writeOpCompositeExtract(componentType, lhs, index, out);
4088*c8dee2aaSAndroid Build Coastguard Worker         SpvId itemR = this->writeOpCompositeExtract(componentType, rhs, index, out);
4089*c8dee2aaSAndroid Build Coastguard Worker         // Use `writeBinaryExpression` with the requested == or != operator on these items.
4090*c8dee2aaSAndroid Build Coastguard Worker         SpvId comparison = this->writeBinaryExpression(componentType, itemL, op,
4091*c8dee2aaSAndroid Build Coastguard Worker                                                        componentType, itemR, boolType, out);
4092*c8dee2aaSAndroid Build Coastguard Worker         // Merge this comparison result with all the other comparisons we've done.
4093*c8dee2aaSAndroid Build Coastguard Worker         allComparisons = this->mergeComparisons(comparison, allComparisons, op, out);
4094*c8dee2aaSAndroid Build Coastguard Worker     }
4095*c8dee2aaSAndroid Build Coastguard Worker     return allComparisons;
4096*c8dee2aaSAndroid Build Coastguard Worker }
4097*c8dee2aaSAndroid Build Coastguard Worker 
writeStructComparison(const Type & structType,SpvId lhs,Operator op,SpvId rhs,OutputStream & out)4098*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeStructComparison(const Type& structType, SpvId lhs, Operator op,
4099*c8dee2aaSAndroid Build Coastguard Worker                                                 SpvId rhs, OutputStream& out) {
4100*c8dee2aaSAndroid Build Coastguard Worker     // The inputs must be structs containing fields, and the op must be == or !=.
4101*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(op.kind() == Operator::Kind::EQEQ || op.kind() == Operator::Kind::NEQ);
4102*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(structType.isStruct());
4103*c8dee2aaSAndroid Build Coastguard Worker     SkSpan<const Field> fields = structType.fields();
4104*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fields.empty());
4105*c8dee2aaSAndroid Build Coastguard Worker 
4106*c8dee2aaSAndroid Build Coastguard Worker     // Synthesize equality checks for each field in the struct.
4107*c8dee2aaSAndroid Build Coastguard Worker     const Type& boolType = *fContext.fTypes.fBool;
4108*c8dee2aaSAndroid Build Coastguard Worker     SpvId allComparisons = NA;
4109*c8dee2aaSAndroid Build Coastguard Worker     for (int index = 0; index < (int)fields.size(); ++index) {
4110*c8dee2aaSAndroid Build Coastguard Worker         // Get the left and right versions of this field.
4111*c8dee2aaSAndroid Build Coastguard Worker         const Type& fieldType = *fields[index].fType;
4112*c8dee2aaSAndroid Build Coastguard Worker 
4113*c8dee2aaSAndroid Build Coastguard Worker         SpvId fieldL = this->writeOpCompositeExtract(fieldType, lhs, index, out);
4114*c8dee2aaSAndroid Build Coastguard Worker         SpvId fieldR = this->writeOpCompositeExtract(fieldType, rhs, index, out);
4115*c8dee2aaSAndroid Build Coastguard Worker         // Use `writeBinaryExpression` with the requested == or != operator on these fields.
4116*c8dee2aaSAndroid Build Coastguard Worker         SpvId comparison = this->writeBinaryExpression(fieldType, fieldL, op, fieldType, fieldR,
4117*c8dee2aaSAndroid Build Coastguard Worker                                                        boolType, out);
4118*c8dee2aaSAndroid Build Coastguard Worker         // Merge this comparison result with all the other comparisons we've done.
4119*c8dee2aaSAndroid Build Coastguard Worker         allComparisons = this->mergeComparisons(comparison, allComparisons, op, out);
4120*c8dee2aaSAndroid Build Coastguard Worker     }
4121*c8dee2aaSAndroid Build Coastguard Worker     return allComparisons;
4122*c8dee2aaSAndroid Build Coastguard Worker }
4123*c8dee2aaSAndroid Build Coastguard Worker 
mergeComparisons(SpvId comparison,SpvId allComparisons,Operator op,OutputStream & out)4124*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::mergeComparisons(SpvId comparison, SpvId allComparisons, Operator op,
4125*c8dee2aaSAndroid Build Coastguard Worker                                            OutputStream& out) {
4126*c8dee2aaSAndroid Build Coastguard Worker     // If this is the first entry, we don't need to merge comparison results with anything.
4127*c8dee2aaSAndroid Build Coastguard Worker     if (allComparisons == NA) {
4128*c8dee2aaSAndroid Build Coastguard Worker         return comparison;
4129*c8dee2aaSAndroid Build Coastguard Worker     }
4130*c8dee2aaSAndroid Build Coastguard Worker     // Use LogicalAnd or LogicalOr to combine the comparison with all the other comparisons.
4131*c8dee2aaSAndroid Build Coastguard Worker     const Type& boolType = *fContext.fTypes.fBool;
4132*c8dee2aaSAndroid Build Coastguard Worker     SpvId boolTypeId = this->getType(boolType);
4133*c8dee2aaSAndroid Build Coastguard Worker     SpvId logicalOp = this->nextId(&boolType);
4134*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
4135*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::EQEQ:
4136*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpLogicalAnd, boolTypeId, logicalOp,
4137*c8dee2aaSAndroid Build Coastguard Worker                                    comparison, allComparisons, out);
4138*c8dee2aaSAndroid Build Coastguard Worker             break;
4139*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::NEQ:
4140*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpLogicalOr, boolTypeId, logicalOp,
4141*c8dee2aaSAndroid Build Coastguard Worker                                    comparison, allComparisons, out);
4142*c8dee2aaSAndroid Build Coastguard Worker             break;
4143*c8dee2aaSAndroid Build Coastguard Worker         default:
4144*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAILF("mergeComparisons only supports == and !=, not %s", op.operatorName());
4145*c8dee2aaSAndroid Build Coastguard Worker             return NA;
4146*c8dee2aaSAndroid Build Coastguard Worker     }
4147*c8dee2aaSAndroid Build Coastguard Worker     return logicalOp;
4148*c8dee2aaSAndroid Build Coastguard Worker }
4149*c8dee2aaSAndroid Build Coastguard Worker 
writeBinaryExpression(const BinaryExpression & b,OutputStream & out)4150*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeBinaryExpression(const BinaryExpression& b, OutputStream& out) {
4151*c8dee2aaSAndroid Build Coastguard Worker     const Expression* left = b.left().get();
4152*c8dee2aaSAndroid Build Coastguard Worker     const Expression* right = b.right().get();
4153*c8dee2aaSAndroid Build Coastguard Worker     Operator op = b.getOperator();
4154*c8dee2aaSAndroid Build Coastguard Worker 
4155*c8dee2aaSAndroid Build Coastguard Worker     switch (op.kind()) {
4156*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::EQ: {
4157*c8dee2aaSAndroid Build Coastguard Worker             // Handles assignment.
4158*c8dee2aaSAndroid Build Coastguard Worker             SpvId rhs = this->writeExpression(*right, out);
4159*c8dee2aaSAndroid Build Coastguard Worker             this->getLValue(*left, out)->store(rhs, out);
4160*c8dee2aaSAndroid Build Coastguard Worker             return rhs;
4161*c8dee2aaSAndroid Build Coastguard Worker         }
4162*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::LOGICALAND:
4163*c8dee2aaSAndroid Build Coastguard Worker             // Handles short-circuiting; we don't necessarily evaluate both LHS and RHS.
4164*c8dee2aaSAndroid Build Coastguard Worker             return this->writeLogicalAnd(*b.left(), *b.right(), out);
4165*c8dee2aaSAndroid Build Coastguard Worker 
4166*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::LOGICALOR:
4167*c8dee2aaSAndroid Build Coastguard Worker             // Handles short-circuiting; we don't necessarily evaluate both LHS and RHS.
4168*c8dee2aaSAndroid Build Coastguard Worker             return this->writeLogicalOr(*b.left(), *b.right(), out);
4169*c8dee2aaSAndroid Build Coastguard Worker 
4170*c8dee2aaSAndroid Build Coastguard Worker         default:
4171*c8dee2aaSAndroid Build Coastguard Worker             break;
4172*c8dee2aaSAndroid Build Coastguard Worker     }
4173*c8dee2aaSAndroid Build Coastguard Worker 
4174*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> lvalue;
4175*c8dee2aaSAndroid Build Coastguard Worker     SpvId lhs;
4176*c8dee2aaSAndroid Build Coastguard Worker     if (op.isAssignment()) {
4177*c8dee2aaSAndroid Build Coastguard Worker         lvalue = this->getLValue(*left, out);
4178*c8dee2aaSAndroid Build Coastguard Worker         lhs = lvalue->load(out);
4179*c8dee2aaSAndroid Build Coastguard Worker     } else {
4180*c8dee2aaSAndroid Build Coastguard Worker         lvalue = nullptr;
4181*c8dee2aaSAndroid Build Coastguard Worker         lhs = this->writeExpression(*left, out);
4182*c8dee2aaSAndroid Build Coastguard Worker     }
4183*c8dee2aaSAndroid Build Coastguard Worker 
4184*c8dee2aaSAndroid Build Coastguard Worker     SpvId rhs = this->writeExpression(*right, out);
4185*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->writeBinaryExpression(left->type(), lhs, op.removeAssignment(),
4186*c8dee2aaSAndroid Build Coastguard Worker                                                right->type(), rhs, b.type(), out);
4187*c8dee2aaSAndroid Build Coastguard Worker     if (lvalue) {
4188*c8dee2aaSAndroid Build Coastguard Worker         lvalue->store(result, out);
4189*c8dee2aaSAndroid Build Coastguard Worker     }
4190*c8dee2aaSAndroid Build Coastguard Worker     return result;
4191*c8dee2aaSAndroid Build Coastguard Worker }
4192*c8dee2aaSAndroid Build Coastguard Worker 
writeLogicalAnd(const Expression & left,const Expression & right,OutputStream & out)4193*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeLogicalAnd(const Expression& left, const Expression& right,
4194*c8dee2aaSAndroid Build Coastguard Worker                                           OutputStream& out) {
4195*c8dee2aaSAndroid Build Coastguard Worker     SpvId falseConstant = this->writeLiteral(0.0, *fContext.fTypes.fBool);
4196*c8dee2aaSAndroid Build Coastguard Worker     SpvId lhs = this->writeExpression(left, out);
4197*c8dee2aaSAndroid Build Coastguard Worker 
4198*c8dee2aaSAndroid Build Coastguard Worker     ConditionalOpCounts conditionalOps = this->getConditionalOpCounts();
4199*c8dee2aaSAndroid Build Coastguard Worker 
4200*c8dee2aaSAndroid Build Coastguard Worker     SpvId rhsLabel = this->nextId(nullptr);
4201*c8dee2aaSAndroid Build Coastguard Worker     SpvId end = this->nextId(nullptr);
4202*c8dee2aaSAndroid Build Coastguard Worker     SpvId lhsBlock = fCurrentBlock;
4203*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
4204*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranchConditional, lhs, rhsLabel, end, out);
4205*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(rhsLabel, kBranchIsOnPreviousLine, out);
4206*c8dee2aaSAndroid Build Coastguard Worker     SpvId rhs = this->writeExpression(right, out);
4207*c8dee2aaSAndroid Build Coastguard Worker     SpvId rhsBlock = fCurrentBlock;
4208*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranch, end, out);
4209*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(end, kBranchIsAbove, conditionalOps, out);
4210*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(nullptr);
4211*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpPhi, this->getType(*fContext.fTypes.fBool), result, falseConstant,
4212*c8dee2aaSAndroid Build Coastguard Worker                            lhsBlock, rhs, rhsBlock, out);
4213*c8dee2aaSAndroid Build Coastguard Worker 
4214*c8dee2aaSAndroid Build Coastguard Worker     return result;
4215*c8dee2aaSAndroid Build Coastguard Worker }
4216*c8dee2aaSAndroid Build Coastguard Worker 
writeLogicalOr(const Expression & left,const Expression & right,OutputStream & out)4217*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeLogicalOr(const Expression& left, const Expression& right,
4218*c8dee2aaSAndroid Build Coastguard Worker                                          OutputStream& out) {
4219*c8dee2aaSAndroid Build Coastguard Worker     SpvId trueConstant = this->writeLiteral(1.0, *fContext.fTypes.fBool);
4220*c8dee2aaSAndroid Build Coastguard Worker     SpvId lhs = this->writeExpression(left, out);
4221*c8dee2aaSAndroid Build Coastguard Worker 
4222*c8dee2aaSAndroid Build Coastguard Worker     ConditionalOpCounts conditionalOps = this->getConditionalOpCounts();
4223*c8dee2aaSAndroid Build Coastguard Worker 
4224*c8dee2aaSAndroid Build Coastguard Worker     SpvId rhsLabel = this->nextId(nullptr);
4225*c8dee2aaSAndroid Build Coastguard Worker     SpvId end = this->nextId(nullptr);
4226*c8dee2aaSAndroid Build Coastguard Worker     SpvId lhsBlock = fCurrentBlock;
4227*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
4228*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranchConditional, lhs, end, rhsLabel, out);
4229*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(rhsLabel, kBranchIsOnPreviousLine, out);
4230*c8dee2aaSAndroid Build Coastguard Worker     SpvId rhs = this->writeExpression(right, out);
4231*c8dee2aaSAndroid Build Coastguard Worker     SpvId rhsBlock = fCurrentBlock;
4232*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranch, end, out);
4233*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(end, kBranchIsAbove, conditionalOps, out);
4234*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(nullptr);
4235*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpPhi, this->getType(*fContext.fTypes.fBool), result, trueConstant,
4236*c8dee2aaSAndroid Build Coastguard Worker                            lhsBlock, rhs, rhsBlock, out);
4237*c8dee2aaSAndroid Build Coastguard Worker 
4238*c8dee2aaSAndroid Build Coastguard Worker     return result;
4239*c8dee2aaSAndroid Build Coastguard Worker }
4240*c8dee2aaSAndroid Build Coastguard Worker 
writeTernaryExpression(const TernaryExpression & t,OutputStream & out)4241*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeTernaryExpression(const TernaryExpression& t, OutputStream& out) {
4242*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = t.type();
4243*c8dee2aaSAndroid Build Coastguard Worker     SpvId test = this->writeExpression(*t.test(), out);
4244*c8dee2aaSAndroid Build Coastguard Worker     if (t.ifTrue()->type().columns() == 1 &&
4245*c8dee2aaSAndroid Build Coastguard Worker         Analysis::IsCompileTimeConstant(*t.ifTrue()) &&
4246*c8dee2aaSAndroid Build Coastguard Worker         Analysis::IsCompileTimeConstant(*t.ifFalse())) {
4247*c8dee2aaSAndroid Build Coastguard Worker         // both true and false are constants, can just use OpSelect
4248*c8dee2aaSAndroid Build Coastguard Worker         SpvId result = this->nextId(nullptr);
4249*c8dee2aaSAndroid Build Coastguard Worker         SpvId trueId = this->writeExpression(*t.ifTrue(), out);
4250*c8dee2aaSAndroid Build Coastguard Worker         SpvId falseId = this->writeExpression(*t.ifFalse(), out);
4251*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpSelect, this->getType(type), result, test, trueId, falseId,
4252*c8dee2aaSAndroid Build Coastguard Worker                                out);
4253*c8dee2aaSAndroid Build Coastguard Worker         return result;
4254*c8dee2aaSAndroid Build Coastguard Worker     }
4255*c8dee2aaSAndroid Build Coastguard Worker 
4256*c8dee2aaSAndroid Build Coastguard Worker     ConditionalOpCounts conditionalOps = this->getConditionalOpCounts();
4257*c8dee2aaSAndroid Build Coastguard Worker 
4258*c8dee2aaSAndroid Build Coastguard Worker     // was originally using OpPhi to choose the result, but for some reason that is crashing on
4259*c8dee2aaSAndroid Build Coastguard Worker     // Adreno. Switched to storing the result in a temp variable as glslang does.
4260*c8dee2aaSAndroid Build Coastguard Worker     SpvId var = this->nextId(nullptr);
4261*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpVariable, this->getPointerType(type, StorageClass::kFunction),
4262*c8dee2aaSAndroid Build Coastguard Worker                            var, SpvStorageClassFunction, fVariableBuffer);
4263*c8dee2aaSAndroid Build Coastguard Worker     SpvId trueLabel = this->nextId(nullptr);
4264*c8dee2aaSAndroid Build Coastguard Worker     SpvId falseLabel = this->nextId(nullptr);
4265*c8dee2aaSAndroid Build Coastguard Worker     SpvId end = this->nextId(nullptr);
4266*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
4267*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranchConditional, test, trueLabel, falseLabel, out);
4268*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(trueLabel, kBranchIsOnPreviousLine, out);
4269*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpStore(StorageClass::kFunction, var, this->writeExpression(*t.ifTrue(), out), out);
4270*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranch, end, out);
4271*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(falseLabel, kBranchIsAbove, conditionalOps, out);
4272*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpStore(StorageClass::kFunction, var, this->writeExpression(*t.ifFalse(), out), out);
4273*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranch, end, out);
4274*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(end, kBranchIsAbove, conditionalOps, out);
4275*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(&type);
4276*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpLoad, this->getType(type), result, var, out);
4277*c8dee2aaSAndroid Build Coastguard Worker 
4278*c8dee2aaSAndroid Build Coastguard Worker     return result;
4279*c8dee2aaSAndroid Build Coastguard Worker }
4280*c8dee2aaSAndroid Build Coastguard Worker 
writePrefixExpression(const PrefixExpression & p,OutputStream & out)4281*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writePrefixExpression(const PrefixExpression& p, OutputStream& out) {
4282*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = p.type();
4283*c8dee2aaSAndroid Build Coastguard Worker     if (p.getOperator().kind() == Operator::Kind::MINUS) {
4284*c8dee2aaSAndroid Build Coastguard Worker         SpvOp_ negateOp = pick_by_type(type, SpvOpFNegate, SpvOpSNegate, SpvOpSNegate, SpvOpUndef);
4285*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(negateOp != SpvOpUndef);
4286*c8dee2aaSAndroid Build Coastguard Worker         SpvId expr = this->writeExpression(*p.operand(), out);
4287*c8dee2aaSAndroid Build Coastguard Worker         if (type.isMatrix()) {
4288*c8dee2aaSAndroid Build Coastguard Worker             return this->writeComponentwiseMatrixUnary(type, expr, negateOp, out);
4289*c8dee2aaSAndroid Build Coastguard Worker         }
4290*c8dee2aaSAndroid Build Coastguard Worker         SpvId result = this->nextId(&type);
4291*c8dee2aaSAndroid Build Coastguard Worker         SpvId typeId = this->getType(type);
4292*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(negateOp, typeId, result, expr, out);
4293*c8dee2aaSAndroid Build Coastguard Worker         return result;
4294*c8dee2aaSAndroid Build Coastguard Worker     }
4295*c8dee2aaSAndroid Build Coastguard Worker     switch (p.getOperator().kind()) {
4296*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::PLUS:
4297*c8dee2aaSAndroid Build Coastguard Worker             return this->writeExpression(*p.operand(), out);
4298*c8dee2aaSAndroid Build Coastguard Worker 
4299*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::PLUSPLUS: {
4300*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<LValue> lv = this->getLValue(*p.operand(), out);
4301*c8dee2aaSAndroid Build Coastguard Worker             SpvId one = this->writeLiteral(1.0, type.componentType());
4302*c8dee2aaSAndroid Build Coastguard Worker             one = this->splat(type, one, out);
4303*c8dee2aaSAndroid Build Coastguard Worker             SpvId result = this->writeBinaryOperationComponentwiseIfMatrix(type, type,
4304*c8dee2aaSAndroid Build Coastguard Worker                                                                            lv->load(out), one,
4305*c8dee2aaSAndroid Build Coastguard Worker                                                                            SpvOpFAdd, SpvOpIAdd,
4306*c8dee2aaSAndroid Build Coastguard Worker                                                                            SpvOpIAdd, SpvOpUndef,
4307*c8dee2aaSAndroid Build Coastguard Worker                                                                            out);
4308*c8dee2aaSAndroid Build Coastguard Worker             lv->store(result, out);
4309*c8dee2aaSAndroid Build Coastguard Worker             return result;
4310*c8dee2aaSAndroid Build Coastguard Worker         }
4311*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::MINUSMINUS: {
4312*c8dee2aaSAndroid Build Coastguard Worker             std::unique_ptr<LValue> lv = this->getLValue(*p.operand(), out);
4313*c8dee2aaSAndroid Build Coastguard Worker             SpvId one = this->writeLiteral(1.0, type.componentType());
4314*c8dee2aaSAndroid Build Coastguard Worker             one = this->splat(type, one, out);
4315*c8dee2aaSAndroid Build Coastguard Worker             SpvId result = this->writeBinaryOperationComponentwiseIfMatrix(type, type,
4316*c8dee2aaSAndroid Build Coastguard Worker                                                                            lv->load(out), one,
4317*c8dee2aaSAndroid Build Coastguard Worker                                                                            SpvOpFSub, SpvOpISub,
4318*c8dee2aaSAndroid Build Coastguard Worker                                                                            SpvOpISub, SpvOpUndef,
4319*c8dee2aaSAndroid Build Coastguard Worker                                                                            out);
4320*c8dee2aaSAndroid Build Coastguard Worker             lv->store(result, out);
4321*c8dee2aaSAndroid Build Coastguard Worker             return result;
4322*c8dee2aaSAndroid Build Coastguard Worker         }
4323*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::LOGICALNOT: {
4324*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(p.operand()->type().isBoolean());
4325*c8dee2aaSAndroid Build Coastguard Worker             SpvId result = this->nextId(nullptr);
4326*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpLogicalNot, this->getType(type), result,
4327*c8dee2aaSAndroid Build Coastguard Worker                                    this->writeExpression(*p.operand(), out), out);
4328*c8dee2aaSAndroid Build Coastguard Worker             return result;
4329*c8dee2aaSAndroid Build Coastguard Worker         }
4330*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::BITWISENOT: {
4331*c8dee2aaSAndroid Build Coastguard Worker             SpvId result = this->nextId(nullptr);
4332*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpNot, this->getType(type), result,
4333*c8dee2aaSAndroid Build Coastguard Worker                                    this->writeExpression(*p.operand(), out), out);
4334*c8dee2aaSAndroid Build Coastguard Worker             return result;
4335*c8dee2aaSAndroid Build Coastguard Worker         }
4336*c8dee2aaSAndroid Build Coastguard Worker         default:
4337*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAILF("unsupported prefix expression: %s",
4338*c8dee2aaSAndroid Build Coastguard Worker                          p.description(OperatorPrecedence::kExpression).c_str());
4339*c8dee2aaSAndroid Build Coastguard Worker             return NA;
4340*c8dee2aaSAndroid Build Coastguard Worker     }
4341*c8dee2aaSAndroid Build Coastguard Worker }
4342*c8dee2aaSAndroid Build Coastguard Worker 
writePostfixExpression(const PostfixExpression & p,OutputStream & out)4343*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writePostfixExpression(const PostfixExpression& p, OutputStream& out) {
4344*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = p.type();
4345*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<LValue> lv = this->getLValue(*p.operand(), out);
4346*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = lv->load(out);
4347*c8dee2aaSAndroid Build Coastguard Worker     SpvId one = this->writeLiteral(1.0, type.componentType());
4348*c8dee2aaSAndroid Build Coastguard Worker     one = this->splat(type, one, out);
4349*c8dee2aaSAndroid Build Coastguard Worker     switch (p.getOperator().kind()) {
4350*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::PLUSPLUS: {
4351*c8dee2aaSAndroid Build Coastguard Worker             SpvId temp = this->writeBinaryOperationComponentwiseIfMatrix(type, type, result, one,
4352*c8dee2aaSAndroid Build Coastguard Worker                                                                          SpvOpFAdd, SpvOpIAdd,
4353*c8dee2aaSAndroid Build Coastguard Worker                                                                          SpvOpIAdd, SpvOpUndef,
4354*c8dee2aaSAndroid Build Coastguard Worker                                                                          out);
4355*c8dee2aaSAndroid Build Coastguard Worker             lv->store(temp, out);
4356*c8dee2aaSAndroid Build Coastguard Worker             return result;
4357*c8dee2aaSAndroid Build Coastguard Worker         }
4358*c8dee2aaSAndroid Build Coastguard Worker         case Operator::Kind::MINUSMINUS: {
4359*c8dee2aaSAndroid Build Coastguard Worker             SpvId temp = this->writeBinaryOperationComponentwiseIfMatrix(type, type, result, one,
4360*c8dee2aaSAndroid Build Coastguard Worker                                                                          SpvOpFSub, SpvOpISub,
4361*c8dee2aaSAndroid Build Coastguard Worker                                                                          SpvOpISub, SpvOpUndef,
4362*c8dee2aaSAndroid Build Coastguard Worker                                                                          out);
4363*c8dee2aaSAndroid Build Coastguard Worker             lv->store(temp, out);
4364*c8dee2aaSAndroid Build Coastguard Worker             return result;
4365*c8dee2aaSAndroid Build Coastguard Worker         }
4366*c8dee2aaSAndroid Build Coastguard Worker         default:
4367*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAILF("unsupported postfix expression %s",
4368*c8dee2aaSAndroid Build Coastguard Worker                          p.description(OperatorPrecedence::kExpression).c_str());
4369*c8dee2aaSAndroid Build Coastguard Worker             return NA;
4370*c8dee2aaSAndroid Build Coastguard Worker     }
4371*c8dee2aaSAndroid Build Coastguard Worker }
4372*c8dee2aaSAndroid Build Coastguard Worker 
writeLiteral(const Literal & l)4373*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeLiteral(const Literal& l) {
4374*c8dee2aaSAndroid Build Coastguard Worker     return this->writeLiteral(l.value(), l.type());
4375*c8dee2aaSAndroid Build Coastguard Worker }
4376*c8dee2aaSAndroid Build Coastguard Worker 
writeLiteral(double value,const Type & type)4377*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeLiteral(double value, const Type& type) {
4378*c8dee2aaSAndroid Build Coastguard Worker     switch (type.numberKind()) {
4379*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kFloat: {
4380*c8dee2aaSAndroid Build Coastguard Worker             float floatVal = value;
4381*c8dee2aaSAndroid Build Coastguard Worker             int32_t valueBits;
4382*c8dee2aaSAndroid Build Coastguard Worker             memcpy(&valueBits, &floatVal, sizeof(valueBits));
4383*c8dee2aaSAndroid Build Coastguard Worker             return this->writeOpConstant(type, valueBits);
4384*c8dee2aaSAndroid Build Coastguard Worker         }
4385*c8dee2aaSAndroid Build Coastguard Worker         case Type::NumberKind::kBoolean: {
4386*c8dee2aaSAndroid Build Coastguard Worker             return value ? this->writeOpConstantTrue(type)
4387*c8dee2aaSAndroid Build Coastguard Worker                          : this->writeOpConstantFalse(type);
4388*c8dee2aaSAndroid Build Coastguard Worker         }
4389*c8dee2aaSAndroid Build Coastguard Worker         default: {
4390*c8dee2aaSAndroid Build Coastguard Worker             return this->writeOpConstant(type, (SKSL_INT)value);
4391*c8dee2aaSAndroid Build Coastguard Worker         }
4392*c8dee2aaSAndroid Build Coastguard Worker     }
4393*c8dee2aaSAndroid Build Coastguard Worker }
4394*c8dee2aaSAndroid Build Coastguard Worker 
writeFunctionStart(const FunctionDeclaration & f,OutputStream & out)4395*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeFunctionStart(const FunctionDeclaration& f, OutputStream& out) {
4396*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = fFunctionMap[{&f, fActiveSpecializationIndex}];
4397*c8dee2aaSAndroid Build Coastguard Worker     SpvId returnTypeId = this->getType(f.returnType());
4398*c8dee2aaSAndroid Build Coastguard Worker     SpvId functionTypeId = this->getFunctionType(f);
4399*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpFunction, returnTypeId, result,
4400*c8dee2aaSAndroid Build Coastguard Worker                            SpvFunctionControlMaskNone, functionTypeId, out);
4401*c8dee2aaSAndroid Build Coastguard Worker     std::string mangledName = f.mangledName();
4402*c8dee2aaSAndroid Build Coastguard Worker 
4403*c8dee2aaSAndroid Build Coastguard Worker     // For specialized functions, tack on `_param1_param2` to the function name.
4404*c8dee2aaSAndroid Build Coastguard Worker     Analysis::GetParameterMappingsForFunction(
4405*c8dee2aaSAndroid Build Coastguard Worker             f, fSpecializationInfo, fActiveSpecializationIndex,
4406*c8dee2aaSAndroid Build Coastguard Worker             [&](int, const Variable*, const Expression* expr) {
4407*c8dee2aaSAndroid Build Coastguard Worker                 std::string name = expr->description();
4408*c8dee2aaSAndroid Build Coastguard Worker                 std::replace_if(name.begin(), name.end(), [](char c) { return !isalnum(c); }, '_');
4409*c8dee2aaSAndroid Build Coastguard Worker 
4410*c8dee2aaSAndroid Build Coastguard Worker                 mangledName += "_" + name;
4411*c8dee2aaSAndroid Build Coastguard Worker             });
4412*c8dee2aaSAndroid Build Coastguard Worker 
4413*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpName,
4414*c8dee2aaSAndroid Build Coastguard Worker                            result,
4415*c8dee2aaSAndroid Build Coastguard Worker                            std::string_view(mangledName.c_str(), mangledName.size()),
4416*c8dee2aaSAndroid Build Coastguard Worker                            fNameBuffer);
4417*c8dee2aaSAndroid Build Coastguard Worker     for (const Variable* parameter : f.parameters()) {
4418*c8dee2aaSAndroid Build Coastguard Worker         const Variable* specializedVar = nullptr;
4419*c8dee2aaSAndroid Build Coastguard Worker         if (fActiveSpecialization) {
4420*c8dee2aaSAndroid Build Coastguard Worker             if (const Expression** specializedExpr = fActiveSpecialization->find(parameter)) {
4421*c8dee2aaSAndroid Build Coastguard Worker                 if ((*specializedExpr)->is<FieldAccess>()) {
4422*c8dee2aaSAndroid Build Coastguard Worker                     continue;
4423*c8dee2aaSAndroid Build Coastguard Worker                 }
4424*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT((*specializedExpr)->is<VariableReference>());
4425*c8dee2aaSAndroid Build Coastguard Worker                 specializedVar = (*specializedExpr)->as<VariableReference>().variable();
4426*c8dee2aaSAndroid Build Coastguard Worker             }
4427*c8dee2aaSAndroid Build Coastguard Worker         }
4428*c8dee2aaSAndroid Build Coastguard Worker 
4429*c8dee2aaSAndroid Build Coastguard Worker         if (fUseTextureSamplerPairs && parameter->type().isSampler()) {
4430*c8dee2aaSAndroid Build Coastguard Worker             auto [texture, sampler] = this->synthesizeTextureAndSampler(*parameter);
4431*c8dee2aaSAndroid Build Coastguard Worker 
4432*c8dee2aaSAndroid Build Coastguard Worker             SpvId textureId = this->nextId(nullptr);
4433*c8dee2aaSAndroid Build Coastguard Worker             fVariableMap.set(texture, textureId);
4434*c8dee2aaSAndroid Build Coastguard Worker 
4435*c8dee2aaSAndroid Build Coastguard Worker             SpvId textureType = this->getFunctionParameterType(texture->type(), texture->layout());
4436*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpFunctionParameter, textureType, textureId, out);
4437*c8dee2aaSAndroid Build Coastguard Worker 
4438*c8dee2aaSAndroid Build Coastguard Worker             if (specializedVar) {
4439*c8dee2aaSAndroid Build Coastguard Worker                 const auto* p = fSynthesizedSamplerMap.find(specializedVar);
4440*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(p);
4441*c8dee2aaSAndroid Build Coastguard Worker                 const SpvId* uniformId = fVariableMap.find((*p)->fSampler.get());
4442*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(uniformId);
4443*c8dee2aaSAndroid Build Coastguard Worker                 fVariableMap.set(sampler, *uniformId);
4444*c8dee2aaSAndroid Build Coastguard Worker             } else {
4445*c8dee2aaSAndroid Build Coastguard Worker                 SpvId samplerId = this->nextId(nullptr);
4446*c8dee2aaSAndroid Build Coastguard Worker                 fVariableMap.set(sampler, samplerId);
4447*c8dee2aaSAndroid Build Coastguard Worker 
4448*c8dee2aaSAndroid Build Coastguard Worker                 SpvId samplerType =
4449*c8dee2aaSAndroid Build Coastguard Worker                         this->getFunctionParameterType(sampler->type(), kDefaultTypeLayout);
4450*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(SpvOpFunctionParameter, samplerType, samplerId, out);
4451*c8dee2aaSAndroid Build Coastguard Worker             }
4452*c8dee2aaSAndroid Build Coastguard Worker         } else {
4453*c8dee2aaSAndroid Build Coastguard Worker             if (specializedVar) {
4454*c8dee2aaSAndroid Build Coastguard Worker                 const SpvId* uniformId = fVariableMap.find(specializedVar);
4455*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(uniformId);
4456*c8dee2aaSAndroid Build Coastguard Worker                 fVariableMap.set(parameter, *uniformId);
4457*c8dee2aaSAndroid Build Coastguard Worker             } else {
4458*c8dee2aaSAndroid Build Coastguard Worker                 SpvId id = this->nextId(nullptr);
4459*c8dee2aaSAndroid Build Coastguard Worker                 fVariableMap.set(parameter, id);
4460*c8dee2aaSAndroid Build Coastguard Worker 
4461*c8dee2aaSAndroid Build Coastguard Worker                 SpvId type = this->getFunctionParameterType(parameter->type(), parameter->layout());
4462*c8dee2aaSAndroid Build Coastguard Worker                 this->writeInstruction(SpvOpFunctionParameter, type, id, out);
4463*c8dee2aaSAndroid Build Coastguard Worker             }
4464*c8dee2aaSAndroid Build Coastguard Worker         }
4465*c8dee2aaSAndroid Build Coastguard Worker     }
4466*c8dee2aaSAndroid Build Coastguard Worker }
4467*c8dee2aaSAndroid Build Coastguard Worker 
writeFunction(const FunctionDefinition & f,OutputStream & out)4468*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeFunction(const FunctionDefinition& f, OutputStream& out) {
4469*c8dee2aaSAndroid Build Coastguard Worker     if (const Analysis::Specializations* specializations =
4470*c8dee2aaSAndroid Build Coastguard Worker                 fSpecializationInfo.fSpecializationMap.find(&f.declaration())) {
4471*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < specializations->size(); i++) {
4472*c8dee2aaSAndroid Build Coastguard Worker             this->writeFunctionInstantiation(f, i, &specializations->at(i), out);
4473*c8dee2aaSAndroid Build Coastguard Worker         }
4474*c8dee2aaSAndroid Build Coastguard Worker     } else {
4475*c8dee2aaSAndroid Build Coastguard Worker         this->writeFunctionInstantiation(f,
4476*c8dee2aaSAndroid Build Coastguard Worker                                          Analysis::kUnspecialized,
4477*c8dee2aaSAndroid Build Coastguard Worker                                          /*specializedParams=*/nullptr,
4478*c8dee2aaSAndroid Build Coastguard Worker                                          out);
4479*c8dee2aaSAndroid Build Coastguard Worker     }
4480*c8dee2aaSAndroid Build Coastguard Worker }
4481*c8dee2aaSAndroid Build Coastguard Worker 
writeFunctionInstantiation(const FunctionDefinition & f,Analysis::SpecializationIndex specializationIndex,const Analysis::SpecializedParameters * specializedParams,OutputStream & out)4482*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeFunctionInstantiation(
4483*c8dee2aaSAndroid Build Coastguard Worker         const FunctionDefinition& f,
4484*c8dee2aaSAndroid Build Coastguard Worker         Analysis::SpecializationIndex specializationIndex,
4485*c8dee2aaSAndroid Build Coastguard Worker         const Analysis::SpecializedParameters* specializedParams,
4486*c8dee2aaSAndroid Build Coastguard Worker         OutputStream& out) {
4487*c8dee2aaSAndroid Build Coastguard Worker     ConditionalOpCounts conditionalOps = this->getConditionalOpCounts();
4488*c8dee2aaSAndroid Build Coastguard Worker 
4489*c8dee2aaSAndroid Build Coastguard Worker     fVariableBuffer.reset();
4490*c8dee2aaSAndroid Build Coastguard Worker     fActiveSpecialization = specializedParams;
4491*c8dee2aaSAndroid Build Coastguard Worker     fActiveSpecializationIndex = specializationIndex;
4492*c8dee2aaSAndroid Build Coastguard Worker     this->writeFunctionStart(f.declaration(), out);
4493*c8dee2aaSAndroid Build Coastguard Worker     fCurrentBlock = 0;
4494*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(this->nextId(nullptr), kBranchlessBlock, out);
4495*c8dee2aaSAndroid Build Coastguard Worker     StringStream bodyBuffer;
4496*c8dee2aaSAndroid Build Coastguard Worker     this->writeBlock(f.body()->as<Block>(), bodyBuffer);
4497*c8dee2aaSAndroid Build Coastguard Worker     fActiveSpecialization = nullptr;
4498*c8dee2aaSAndroid Build Coastguard Worker     fActiveSpecializationIndex = Analysis::kUnspecialized;
4499*c8dee2aaSAndroid Build Coastguard Worker     write_stringstream(fVariableBuffer, out);
4500*c8dee2aaSAndroid Build Coastguard Worker     if (f.declaration().isMain()) {
4501*c8dee2aaSAndroid Build Coastguard Worker         write_stringstream(fGlobalInitializersBuffer, out);
4502*c8dee2aaSAndroid Build Coastguard Worker     }
4503*c8dee2aaSAndroid Build Coastguard Worker     write_stringstream(bodyBuffer, out);
4504*c8dee2aaSAndroid Build Coastguard Worker     if (fCurrentBlock) {
4505*c8dee2aaSAndroid Build Coastguard Worker         if (f.declaration().returnType().isVoid()) {
4506*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpReturn, out);
4507*c8dee2aaSAndroid Build Coastguard Worker         } else {
4508*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpUnreachable, out);
4509*c8dee2aaSAndroid Build Coastguard Worker         }
4510*c8dee2aaSAndroid Build Coastguard Worker     }
4511*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpFunctionEnd, out);
4512*c8dee2aaSAndroid Build Coastguard Worker     this->pruneConditionalOps(conditionalOps);
4513*c8dee2aaSAndroid Build Coastguard Worker }
4514*c8dee2aaSAndroid Build Coastguard Worker 
writeLayout(const Layout & layout,SpvId target,Position pos)4515*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeLayout(const Layout& layout, SpvId target, Position pos) {
4516*c8dee2aaSAndroid Build Coastguard Worker     bool isPushConstant = SkToBool(layout.fFlags & LayoutFlag::kPushConstant);
4517*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fLocation >= 0) {
4518*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpDecorate, target, SpvDecorationLocation, layout.fLocation,
4519*c8dee2aaSAndroid Build Coastguard Worker                                fDecorationBuffer);
4520*c8dee2aaSAndroid Build Coastguard Worker     }
4521*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fBinding >= 0) {
4522*c8dee2aaSAndroid Build Coastguard Worker         if (isPushConstant) {
4523*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(pos, "Can't apply 'binding' to push constants");
4524*c8dee2aaSAndroid Build Coastguard Worker         } else {
4525*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpDecorate, target, SpvDecorationBinding, layout.fBinding,
4526*c8dee2aaSAndroid Build Coastguard Worker                                    fDecorationBuffer);
4527*c8dee2aaSAndroid Build Coastguard Worker         }
4528*c8dee2aaSAndroid Build Coastguard Worker     }
4529*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fIndex >= 0) {
4530*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpDecorate, target, SpvDecorationIndex, layout.fIndex,
4531*c8dee2aaSAndroid Build Coastguard Worker                                fDecorationBuffer);
4532*c8dee2aaSAndroid Build Coastguard Worker     }
4533*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fSet >= 0) {
4534*c8dee2aaSAndroid Build Coastguard Worker         if (isPushConstant) {
4535*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(pos, "Can't apply 'set' to push constants");
4536*c8dee2aaSAndroid Build Coastguard Worker         } else {
4537*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpDecorate, target, SpvDecorationDescriptorSet, layout.fSet,
4538*c8dee2aaSAndroid Build Coastguard Worker                                    fDecorationBuffer);
4539*c8dee2aaSAndroid Build Coastguard Worker         }
4540*c8dee2aaSAndroid Build Coastguard Worker     }
4541*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fInputAttachmentIndex >= 0) {
4542*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpDecorate, target, SpvDecorationInputAttachmentIndex,
4543*c8dee2aaSAndroid Build Coastguard Worker                                layout.fInputAttachmentIndex, fDecorationBuffer);
4544*c8dee2aaSAndroid Build Coastguard Worker         fCapabilities |= (((uint64_t) 1) << SpvCapabilityInputAttachment);
4545*c8dee2aaSAndroid Build Coastguard Worker     }
4546*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fBuiltin >= 0 && (layout.fBuiltin != SK_FRAGCOLOR_BUILTIN &&
4547*c8dee2aaSAndroid Build Coastguard Worker                                  layout.fBuiltin != SK_SECONDARYFRAGCOLOR_BUILTIN)) {
4548*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpDecorate, target, SpvDecorationBuiltIn, layout.fBuiltin,
4549*c8dee2aaSAndroid Build Coastguard Worker                                    fDecorationBuffer);
4550*c8dee2aaSAndroid Build Coastguard Worker     }
4551*c8dee2aaSAndroid Build Coastguard Worker }
4552*c8dee2aaSAndroid Build Coastguard Worker 
writeFieldLayout(const Layout & layout,SpvId target,int member)4553*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeFieldLayout(const Layout& layout, SpvId target, int member) {
4554*c8dee2aaSAndroid Build Coastguard Worker     // 'binding' and 'set' can not be applied to struct members
4555*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(layout.fBinding == -1);
4556*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(layout.fSet == -1);
4557*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fLocation >= 0) {
4558*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationLocation,
4559*c8dee2aaSAndroid Build Coastguard Worker                                layout.fLocation, fDecorationBuffer);
4560*c8dee2aaSAndroid Build Coastguard Worker     }
4561*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fIndex >= 0) {
4562*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationIndex,
4563*c8dee2aaSAndroid Build Coastguard Worker                                layout.fIndex, fDecorationBuffer);
4564*c8dee2aaSAndroid Build Coastguard Worker     }
4565*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fInputAttachmentIndex >= 0) {
4566*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpDecorate, target, member, SpvDecorationInputAttachmentIndex,
4567*c8dee2aaSAndroid Build Coastguard Worker                                layout.fInputAttachmentIndex, fDecorationBuffer);
4568*c8dee2aaSAndroid Build Coastguard Worker     }
4569*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fBuiltin >= 0) {
4570*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpMemberDecorate, target, member, SpvDecorationBuiltIn,
4571*c8dee2aaSAndroid Build Coastguard Worker                                layout.fBuiltin, fDecorationBuffer);
4572*c8dee2aaSAndroid Build Coastguard Worker     }
4573*c8dee2aaSAndroid Build Coastguard Worker }
4574*c8dee2aaSAndroid Build Coastguard Worker 
memoryLayoutForStorageClass(StorageClass storageClass)4575*c8dee2aaSAndroid Build Coastguard Worker MemoryLayout SPIRVCodeGenerator::memoryLayoutForStorageClass(StorageClass storageClass) {
4576*c8dee2aaSAndroid Build Coastguard Worker     return storageClass == StorageClass::kPushConstant ||
4577*c8dee2aaSAndroid Build Coastguard Worker            storageClass == StorageClass::kStorageBuffer
4578*c8dee2aaSAndroid Build Coastguard Worker                                 ? MemoryLayout(MemoryLayout::Standard::k430)
4579*c8dee2aaSAndroid Build Coastguard Worker                                 : fDefaultMemoryLayout;
4580*c8dee2aaSAndroid Build Coastguard Worker }
4581*c8dee2aaSAndroid Build Coastguard Worker 
memoryLayoutForVariable(const Variable & v) const4582*c8dee2aaSAndroid Build Coastguard Worker MemoryLayout SPIRVCodeGenerator::memoryLayoutForVariable(const Variable& v) const {
4583*c8dee2aaSAndroid Build Coastguard Worker     bool pushConstant = SkToBool(v.layout().fFlags & LayoutFlag::kPushConstant);
4584*c8dee2aaSAndroid Build Coastguard Worker     bool buffer = v.modifierFlags().isBuffer();
4585*c8dee2aaSAndroid Build Coastguard Worker     return pushConstant || buffer ? MemoryLayout(MemoryLayout::Standard::k430)
4586*c8dee2aaSAndroid Build Coastguard Worker                                   : fDefaultMemoryLayout;
4587*c8dee2aaSAndroid Build Coastguard Worker }
4588*c8dee2aaSAndroid Build Coastguard Worker 
writeInterfaceBlock(const InterfaceBlock & intf,bool appendRTFlip)4589*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeInterfaceBlock(const InterfaceBlock& intf, bool appendRTFlip) {
4590*c8dee2aaSAndroid Build Coastguard Worker     MemoryLayout memoryLayout = this->memoryLayoutForVariable(*intf.var());
4591*c8dee2aaSAndroid Build Coastguard Worker     SpvId result = this->nextId(nullptr);
4592*c8dee2aaSAndroid Build Coastguard Worker     const Variable& intfVar = *intf.var();
4593*c8dee2aaSAndroid Build Coastguard Worker     const Type& type = intfVar.type();
4594*c8dee2aaSAndroid Build Coastguard Worker     if (!memoryLayout.isSupported(type)) {
4595*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(type.fPosition, "type '" + type.displayName() +
4596*c8dee2aaSAndroid Build Coastguard Worker                                                 "' is not permitted here");
4597*c8dee2aaSAndroid Build Coastguard Worker         return this->nextId(nullptr);
4598*c8dee2aaSAndroid Build Coastguard Worker     }
4599*c8dee2aaSAndroid Build Coastguard Worker     StorageClass storageClass =
4600*c8dee2aaSAndroid Build Coastguard Worker             get_storage_class_for_global_variable(intfVar, StorageClass::kFunction);
4601*c8dee2aaSAndroid Build Coastguard Worker     if (fProgram.fInterface.fRTFlipUniform != Program::Interface::kRTFlip_None && appendRTFlip &&
4602*c8dee2aaSAndroid Build Coastguard Worker         !fWroteRTFlip && type.isStruct()) {
4603*c8dee2aaSAndroid Build Coastguard Worker         // We can only have one interface block (because we use push_constant and that is limited
4604*c8dee2aaSAndroid Build Coastguard Worker         // to one per program), so we need to append rtflip to this one rather than synthesize an
4605*c8dee2aaSAndroid Build Coastguard Worker         // entirely new block when the variable is referenced. And we can't modify the existing
4606*c8dee2aaSAndroid Build Coastguard Worker         // block, so we instead create a modified copy of it and write that.
4607*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<const Field> fieldSpan = type.fields();
4608*c8dee2aaSAndroid Build Coastguard Worker         TArray<Field> fields(fieldSpan.data(), fieldSpan.size());
4609*c8dee2aaSAndroid Build Coastguard Worker         fields.emplace_back(Position(),
4610*c8dee2aaSAndroid Build Coastguard Worker                             Layout(LayoutFlag::kNone,
4611*c8dee2aaSAndroid Build Coastguard Worker                                    /*location=*/-1,
4612*c8dee2aaSAndroid Build Coastguard Worker                                    fProgram.fConfig->fSettings.fRTFlipOffset,
4613*c8dee2aaSAndroid Build Coastguard Worker                                    /*binding=*/-1,
4614*c8dee2aaSAndroid Build Coastguard Worker                                    /*index=*/-1,
4615*c8dee2aaSAndroid Build Coastguard Worker                                    /*set=*/-1,
4616*c8dee2aaSAndroid Build Coastguard Worker                                    /*builtin=*/-1,
4617*c8dee2aaSAndroid Build Coastguard Worker                                    /*inputAttachmentIndex=*/-1),
4618*c8dee2aaSAndroid Build Coastguard Worker                             ModifierFlag::kNone,
4619*c8dee2aaSAndroid Build Coastguard Worker                             SKSL_RTFLIP_NAME,
4620*c8dee2aaSAndroid Build Coastguard Worker                             fContext.fTypes.fFloat2.get());
4621*c8dee2aaSAndroid Build Coastguard Worker         {
4622*c8dee2aaSAndroid Build Coastguard Worker             AutoAttachPoolToThread attach(fProgram.fPool.get());
4623*c8dee2aaSAndroid Build Coastguard Worker             const Type* rtFlipStructType = fProgram.fSymbols->takeOwnershipOfSymbol(
4624*c8dee2aaSAndroid Build Coastguard Worker                     Type::MakeStructType(fContext,
4625*c8dee2aaSAndroid Build Coastguard Worker                                          type.fPosition,
4626*c8dee2aaSAndroid Build Coastguard Worker                                          type.name(),
4627*c8dee2aaSAndroid Build Coastguard Worker                                          std::move(fields),
4628*c8dee2aaSAndroid Build Coastguard Worker                                          /*interfaceBlock=*/true));
4629*c8dee2aaSAndroid Build Coastguard Worker             Variable* modifiedVar = fProgram.fSymbols->takeOwnershipOfSymbol(
4630*c8dee2aaSAndroid Build Coastguard Worker                     Variable::Make(intfVar.fPosition,
4631*c8dee2aaSAndroid Build Coastguard Worker                                    intfVar.modifiersPosition(),
4632*c8dee2aaSAndroid Build Coastguard Worker                                    intfVar.layout(),
4633*c8dee2aaSAndroid Build Coastguard Worker                                    intfVar.modifierFlags(),
4634*c8dee2aaSAndroid Build Coastguard Worker                                    rtFlipStructType,
4635*c8dee2aaSAndroid Build Coastguard Worker                                    intfVar.name(),
4636*c8dee2aaSAndroid Build Coastguard Worker                                    /*mangledName=*/"",
4637*c8dee2aaSAndroid Build Coastguard Worker                                    intfVar.isBuiltin(),
4638*c8dee2aaSAndroid Build Coastguard Worker                                    intfVar.storage()));
4639*c8dee2aaSAndroid Build Coastguard Worker             InterfaceBlock modifiedCopy(intf.fPosition, modifiedVar);
4640*c8dee2aaSAndroid Build Coastguard Worker             result = this->writeInterfaceBlock(modifiedCopy, /*appendRTFlip=*/false);
4641*c8dee2aaSAndroid Build Coastguard Worker             fProgram.fSymbols->add(fContext, std::make_unique<FieldSymbol>(
4642*c8dee2aaSAndroid Build Coastguard Worker                     Position(), modifiedVar, rtFlipStructType->fields().size() - 1));
4643*c8dee2aaSAndroid Build Coastguard Worker         }
4644*c8dee2aaSAndroid Build Coastguard Worker         fVariableMap.set(&intfVar, result);
4645*c8dee2aaSAndroid Build Coastguard Worker         fWroteRTFlip = true;
4646*c8dee2aaSAndroid Build Coastguard Worker         return result;
4647*c8dee2aaSAndroid Build Coastguard Worker     }
4648*c8dee2aaSAndroid Build Coastguard Worker     SpvId typeId = this->getType(type, kDefaultTypeLayout, memoryLayout);
4649*c8dee2aaSAndroid Build Coastguard Worker     if (intfVar.layout().fBuiltin == -1) {
4650*c8dee2aaSAndroid Build Coastguard Worker         // Note: In SPIR-V 1.3, a storage buffer can be declared with the "StorageBuffer"
4651*c8dee2aaSAndroid Build Coastguard Worker         // storage class and the "Block" decoration and the <1.3 approach we use here ("Uniform"
4652*c8dee2aaSAndroid Build Coastguard Worker         // storage class and the "BufferBlock" decoration) is deprecated. Since we target SPIR-V
4653*c8dee2aaSAndroid Build Coastguard Worker         // 1.0, we have to use the deprecated approach which is well supported in Vulkan and
4654*c8dee2aaSAndroid Build Coastguard Worker         // addresses SkSL use cases (notably SkSL currently doesn't support pointer features that
4655*c8dee2aaSAndroid Build Coastguard Worker         // would benefit from SPV_KHR_variable_pointers capabilities).
4656*c8dee2aaSAndroid Build Coastguard Worker         bool isStorageBuffer = intfVar.modifierFlags().isBuffer();
4657*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpDecorate,
4658*c8dee2aaSAndroid Build Coastguard Worker                                typeId,
4659*c8dee2aaSAndroid Build Coastguard Worker                                isStorageBuffer ? SpvDecorationBufferBlock : SpvDecorationBlock,
4660*c8dee2aaSAndroid Build Coastguard Worker                                fDecorationBuffer);
4661*c8dee2aaSAndroid Build Coastguard Worker     }
4662*c8dee2aaSAndroid Build Coastguard Worker     SpvId ptrType = this->nextId(nullptr);
4663*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpTypePointer, ptrType,
4664*c8dee2aaSAndroid Build Coastguard Worker                            get_storage_class_spv_id(storageClass), typeId, fConstantBuffer);
4665*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpVariable, ptrType, result,
4666*c8dee2aaSAndroid Build Coastguard Worker                            get_storage_class_spv_id(storageClass), fConstantBuffer);
4667*c8dee2aaSAndroid Build Coastguard Worker     Layout layout = intfVar.layout();
4668*c8dee2aaSAndroid Build Coastguard Worker     if ((storageClass == StorageClass::kUniform ||
4669*c8dee2aaSAndroid Build Coastguard Worker                 storageClass == StorageClass::kStorageBuffer) && layout.fSet < 0) {
4670*c8dee2aaSAndroid Build Coastguard Worker         layout.fSet = fProgram.fConfig->fSettings.fDefaultUniformSet;
4671*c8dee2aaSAndroid Build Coastguard Worker     }
4672*c8dee2aaSAndroid Build Coastguard Worker     this->writeLayout(layout, result, intfVar.fPosition);
4673*c8dee2aaSAndroid Build Coastguard Worker     fVariableMap.set(&intfVar, result);
4674*c8dee2aaSAndroid Build Coastguard Worker     return result;
4675*c8dee2aaSAndroid Build Coastguard Worker }
4676*c8dee2aaSAndroid Build Coastguard Worker 
4677*c8dee2aaSAndroid Build Coastguard Worker // This function determines whether to skip an OpVariable (of pointer type) declaration for
4678*c8dee2aaSAndroid Build Coastguard Worker // compile-time constant scalars and vectors which we turn into OpConstant/OpConstantComposite and
4679*c8dee2aaSAndroid Build Coastguard Worker // always reference by value.
4680*c8dee2aaSAndroid Build Coastguard Worker //
4681*c8dee2aaSAndroid Build Coastguard Worker // Accessing a matrix or array member with a dynamic index requires the use of OpAccessChain which
4682*c8dee2aaSAndroid Build Coastguard Worker // requires a base operand of pointer type. However, a vector can always be accessed by value using
4683*c8dee2aaSAndroid Build Coastguard Worker // OpVectorExtractDynamic (see writeIndexExpression).
4684*c8dee2aaSAndroid Build Coastguard Worker //
4685*c8dee2aaSAndroid Build Coastguard Worker // This is why we always emit an OpVariable for all non-scalar and non-vector types in case they get
4686*c8dee2aaSAndroid Build Coastguard Worker // accessed via a dynamic index.
is_vardecl_compile_time_constant(const VarDeclaration & varDecl)4687*c8dee2aaSAndroid Build Coastguard Worker static bool is_vardecl_compile_time_constant(const VarDeclaration& varDecl) {
4688*c8dee2aaSAndroid Build Coastguard Worker     return varDecl.var()->modifierFlags().isConst() &&
4689*c8dee2aaSAndroid Build Coastguard Worker            (varDecl.var()->type().isScalar() || varDecl.var()->type().isVector()) &&
4690*c8dee2aaSAndroid Build Coastguard Worker            (ConstantFolder::GetConstantValueOrNull(*varDecl.value()) ||
4691*c8dee2aaSAndroid Build Coastguard Worker             Analysis::IsCompileTimeConstant(*varDecl.value()));
4692*c8dee2aaSAndroid Build Coastguard Worker }
4693*c8dee2aaSAndroid Build Coastguard Worker 
writeGlobalVarDeclaration(ProgramKind kind,const VarDeclaration & varDecl)4694*c8dee2aaSAndroid Build Coastguard Worker bool SPIRVCodeGenerator::writeGlobalVarDeclaration(ProgramKind kind,
4695*c8dee2aaSAndroid Build Coastguard Worker                                                    const VarDeclaration& varDecl) {
4696*c8dee2aaSAndroid Build Coastguard Worker     const Variable* var = varDecl.var();
4697*c8dee2aaSAndroid Build Coastguard Worker     const LayoutFlags backendFlags = var->layout().fFlags & LayoutFlag::kAllBackends;
4698*c8dee2aaSAndroid Build Coastguard Worker     const LayoutFlags kPermittedBackendFlags =
4699*c8dee2aaSAndroid Build Coastguard Worker             LayoutFlag::kVulkan | LayoutFlag::kWebGPU | LayoutFlag::kDirect3D;
4700*c8dee2aaSAndroid Build Coastguard Worker     if (backendFlags & ~kPermittedBackendFlags) {
4701*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(var->fPosition, "incompatible backend flag in SPIR-V codegen");
4702*c8dee2aaSAndroid Build Coastguard Worker         return false;
4703*c8dee2aaSAndroid Build Coastguard Worker     }
4704*c8dee2aaSAndroid Build Coastguard Worker 
4705*c8dee2aaSAndroid Build Coastguard Worker     // If this global variable is a compile-time constant then we'll emit OpConstant or
4706*c8dee2aaSAndroid Build Coastguard Worker     // OpConstantComposite later when the variable is referenced. Avoid declaring an OpVariable now.
4707*c8dee2aaSAndroid Build Coastguard Worker     if (is_vardecl_compile_time_constant(varDecl)) {
4708*c8dee2aaSAndroid Build Coastguard Worker         return true;
4709*c8dee2aaSAndroid Build Coastguard Worker     }
4710*c8dee2aaSAndroid Build Coastguard Worker 
4711*c8dee2aaSAndroid Build Coastguard Worker     StorageClass storageClass =
4712*c8dee2aaSAndroid Build Coastguard Worker             get_storage_class_for_global_variable(*var, StorageClass::kPrivate);
4713*c8dee2aaSAndroid Build Coastguard Worker     if (storageClass == StorageClass::kUniform || storageClass == StorageClass::kStorageBuffer) {
4714*c8dee2aaSAndroid Build Coastguard Worker         // Top-level uniforms are emitted in writeUniformBuffer.
4715*c8dee2aaSAndroid Build Coastguard Worker         fTopLevelUniforms.push_back(&varDecl);
4716*c8dee2aaSAndroid Build Coastguard Worker         return true;
4717*c8dee2aaSAndroid Build Coastguard Worker     }
4718*c8dee2aaSAndroid Build Coastguard Worker 
4719*c8dee2aaSAndroid Build Coastguard Worker     if (fUseTextureSamplerPairs && var->type().isSampler()) {
4720*c8dee2aaSAndroid Build Coastguard Worker         if (var->layout().fTexture == -1 || var->layout().fSampler == -1) {
4721*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(var->fPosition, "selected backend requires separate texture "
4722*c8dee2aaSAndroid Build Coastguard Worker                                                     "and sampler indices");
4723*c8dee2aaSAndroid Build Coastguard Worker             return false;
4724*c8dee2aaSAndroid Build Coastguard Worker         }
4725*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(storageClass == StorageClass::kUniformConstant);
4726*c8dee2aaSAndroid Build Coastguard Worker 
4727*c8dee2aaSAndroid Build Coastguard Worker         auto [texture, sampler] = this->synthesizeTextureAndSampler(*var);
4728*c8dee2aaSAndroid Build Coastguard Worker         this->writeGlobalVar(kind, storageClass, *texture);
4729*c8dee2aaSAndroid Build Coastguard Worker         this->writeGlobalVar(kind, storageClass, *sampler);
4730*c8dee2aaSAndroid Build Coastguard Worker 
4731*c8dee2aaSAndroid Build Coastguard Worker         return true;
4732*c8dee2aaSAndroid Build Coastguard Worker     }
4733*c8dee2aaSAndroid Build Coastguard Worker 
4734*c8dee2aaSAndroid Build Coastguard Worker     SpvId id = this->writeGlobalVar(kind, storageClass, *var);
4735*c8dee2aaSAndroid Build Coastguard Worker     if (id != NA && varDecl.value()) {
4736*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!fCurrentBlock);
4737*c8dee2aaSAndroid Build Coastguard Worker         fCurrentBlock = NA;
4738*c8dee2aaSAndroid Build Coastguard Worker         SpvId value = this->writeExpression(*varDecl.value(), fGlobalInitializersBuffer);
4739*c8dee2aaSAndroid Build Coastguard Worker         this->writeOpStore(storageClass, id, value, fGlobalInitializersBuffer);
4740*c8dee2aaSAndroid Build Coastguard Worker         fCurrentBlock = 0;
4741*c8dee2aaSAndroid Build Coastguard Worker     }
4742*c8dee2aaSAndroid Build Coastguard Worker     return true;
4743*c8dee2aaSAndroid Build Coastguard Worker }
4744*c8dee2aaSAndroid Build Coastguard Worker 
writeGlobalVar(ProgramKind kind,StorageClass storageClass,const Variable & var)4745*c8dee2aaSAndroid Build Coastguard Worker SpvId SPIRVCodeGenerator::writeGlobalVar(ProgramKind kind,
4746*c8dee2aaSAndroid Build Coastguard Worker                                          StorageClass storageClass,
4747*c8dee2aaSAndroid Build Coastguard Worker                                          const Variable& var) {
4748*c8dee2aaSAndroid Build Coastguard Worker     Layout layout = var.layout();
4749*c8dee2aaSAndroid Build Coastguard Worker     const ModifierFlags flags = var.modifierFlags();
4750*c8dee2aaSAndroid Build Coastguard Worker     const Type* type = &var.type();
4751*c8dee2aaSAndroid Build Coastguard Worker     switch (layout.fBuiltin) {
4752*c8dee2aaSAndroid Build Coastguard Worker         case SK_FRAGCOLOR_BUILTIN:
4753*c8dee2aaSAndroid Build Coastguard Worker         case SK_SECONDARYFRAGCOLOR_BUILTIN:
4754*c8dee2aaSAndroid Build Coastguard Worker             if (!ProgramConfig::IsFragment(kind)) {
4755*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!fProgram.fConfig->fSettings.fFragColorIsInOut);
4756*c8dee2aaSAndroid Build Coastguard Worker                 return NA;
4757*c8dee2aaSAndroid Build Coastguard Worker             }
4758*c8dee2aaSAndroid Build Coastguard Worker             break;
4759*c8dee2aaSAndroid Build Coastguard Worker 
4760*c8dee2aaSAndroid Build Coastguard Worker         case SK_SAMPLEMASKIN_BUILTIN:
4761*c8dee2aaSAndroid Build Coastguard Worker         case SK_SAMPLEMASK_BUILTIN:
4762*c8dee2aaSAndroid Build Coastguard Worker             // SkSL exposes this as a `uint` but SPIR-V, like GLSL, uses an array of signed `uint`
4763*c8dee2aaSAndroid Build Coastguard Worker             // decorated with SpvBuiltinSampleMask.
4764*c8dee2aaSAndroid Build Coastguard Worker             type = fSynthetics.addArrayDimension(fContext, type, /*arraySize=*/1);
4765*c8dee2aaSAndroid Build Coastguard Worker             layout.fBuiltin = SpvBuiltInSampleMask;
4766*c8dee2aaSAndroid Build Coastguard Worker             break;
4767*c8dee2aaSAndroid Build Coastguard Worker     }
4768*c8dee2aaSAndroid Build Coastguard Worker 
4769*c8dee2aaSAndroid Build Coastguard Worker     // Add this global to the variable map.
4770*c8dee2aaSAndroid Build Coastguard Worker     SpvId id = this->nextId(type);
4771*c8dee2aaSAndroid Build Coastguard Worker     fVariableMap.set(&var, id);
4772*c8dee2aaSAndroid Build Coastguard Worker 
4773*c8dee2aaSAndroid Build Coastguard Worker     if (layout.fSet < 0 && storageClass == StorageClass::kUniformConstant) {
4774*c8dee2aaSAndroid Build Coastguard Worker         layout.fSet = fProgram.fConfig->fSettings.fDefaultUniformSet;
4775*c8dee2aaSAndroid Build Coastguard Worker     }
4776*c8dee2aaSAndroid Build Coastguard Worker 
4777*c8dee2aaSAndroid Build Coastguard Worker     SpvId typeId = this->getPointerType(*type,
4778*c8dee2aaSAndroid Build Coastguard Worker                                         layout,
4779*c8dee2aaSAndroid Build Coastguard Worker                                         this->memoryLayoutForStorageClass(storageClass),
4780*c8dee2aaSAndroid Build Coastguard Worker                                         storageClass);
4781*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpVariable, typeId, id,
4782*c8dee2aaSAndroid Build Coastguard Worker                            get_storage_class_spv_id(storageClass), fConstantBuffer);
4783*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpName, id, var.name(), fNameBuffer);
4784*c8dee2aaSAndroid Build Coastguard Worker     this->writeLayout(layout, id, var.fPosition);
4785*c8dee2aaSAndroid Build Coastguard Worker     if (flags & ModifierFlag::kFlat) {
4786*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpDecorate, id, SpvDecorationFlat, fDecorationBuffer);
4787*c8dee2aaSAndroid Build Coastguard Worker     }
4788*c8dee2aaSAndroid Build Coastguard Worker     if (flags & ModifierFlag::kNoPerspective) {
4789*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpDecorate, id, SpvDecorationNoPerspective,
4790*c8dee2aaSAndroid Build Coastguard Worker                                fDecorationBuffer);
4791*c8dee2aaSAndroid Build Coastguard Worker     }
4792*c8dee2aaSAndroid Build Coastguard Worker     if (flags.isWriteOnly()) {
4793*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpDecorate, id, SpvDecorationNonReadable, fDecorationBuffer);
4794*c8dee2aaSAndroid Build Coastguard Worker     } else if (flags.isReadOnly()) {
4795*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpDecorate, id, SpvDecorationNonWritable, fDecorationBuffer);
4796*c8dee2aaSAndroid Build Coastguard Worker     }
4797*c8dee2aaSAndroid Build Coastguard Worker 
4798*c8dee2aaSAndroid Build Coastguard Worker     return id;
4799*c8dee2aaSAndroid Build Coastguard Worker }
4800*c8dee2aaSAndroid Build Coastguard Worker 
writeVarDeclaration(const VarDeclaration & varDecl,OutputStream & out)4801*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeVarDeclaration(const VarDeclaration& varDecl, OutputStream& out) {
4802*c8dee2aaSAndroid Build Coastguard Worker     // If this variable is a compile-time constant then we'll emit OpConstant or
4803*c8dee2aaSAndroid Build Coastguard Worker     // OpConstantComposite later when the variable is referenced. Avoid declaring an OpVariable now.
4804*c8dee2aaSAndroid Build Coastguard Worker     if (is_vardecl_compile_time_constant(varDecl)) {
4805*c8dee2aaSAndroid Build Coastguard Worker         return;
4806*c8dee2aaSAndroid Build Coastguard Worker     }
4807*c8dee2aaSAndroid Build Coastguard Worker 
4808*c8dee2aaSAndroid Build Coastguard Worker     const Variable* var = varDecl.var();
4809*c8dee2aaSAndroid Build Coastguard Worker     SpvId id = this->nextId(&var->type());
4810*c8dee2aaSAndroid Build Coastguard Worker     fVariableMap.set(var, id);
4811*c8dee2aaSAndroid Build Coastguard Worker     SpvId type = this->getPointerType(var->type(), StorageClass::kFunction);
4812*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpVariable, type, id, SpvStorageClassFunction, fVariableBuffer);
4813*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpName, id, var->name(), fNameBuffer);
4814*c8dee2aaSAndroid Build Coastguard Worker     if (varDecl.value()) {
4815*c8dee2aaSAndroid Build Coastguard Worker         SpvId value = this->writeExpression(*varDecl.value(), out);
4816*c8dee2aaSAndroid Build Coastguard Worker         this->writeOpStore(StorageClass::kFunction, id, value, out);
4817*c8dee2aaSAndroid Build Coastguard Worker     }
4818*c8dee2aaSAndroid Build Coastguard Worker }
4819*c8dee2aaSAndroid Build Coastguard Worker 
writeStatement(const Statement & s,OutputStream & out)4820*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeStatement(const Statement& s, OutputStream& out) {
4821*c8dee2aaSAndroid Build Coastguard Worker     switch (s.kind()) {
4822*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kNop:
4823*c8dee2aaSAndroid Build Coastguard Worker             break;
4824*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kBlock:
4825*c8dee2aaSAndroid Build Coastguard Worker             this->writeBlock(s.as<Block>(), out);
4826*c8dee2aaSAndroid Build Coastguard Worker             break;
4827*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kExpression:
4828*c8dee2aaSAndroid Build Coastguard Worker             this->writeExpression(*s.as<ExpressionStatement>().expression(), out);
4829*c8dee2aaSAndroid Build Coastguard Worker             break;
4830*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kReturn:
4831*c8dee2aaSAndroid Build Coastguard Worker             this->writeReturnStatement(s.as<ReturnStatement>(), out);
4832*c8dee2aaSAndroid Build Coastguard Worker             break;
4833*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kVarDeclaration:
4834*c8dee2aaSAndroid Build Coastguard Worker             this->writeVarDeclaration(s.as<VarDeclaration>(), out);
4835*c8dee2aaSAndroid Build Coastguard Worker             break;
4836*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kIf:
4837*c8dee2aaSAndroid Build Coastguard Worker             this->writeIfStatement(s.as<IfStatement>(), out);
4838*c8dee2aaSAndroid Build Coastguard Worker             break;
4839*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kFor:
4840*c8dee2aaSAndroid Build Coastguard Worker             this->writeForStatement(s.as<ForStatement>(), out);
4841*c8dee2aaSAndroid Build Coastguard Worker             break;
4842*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kDo:
4843*c8dee2aaSAndroid Build Coastguard Worker             this->writeDoStatement(s.as<DoStatement>(), out);
4844*c8dee2aaSAndroid Build Coastguard Worker             break;
4845*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kSwitch:
4846*c8dee2aaSAndroid Build Coastguard Worker             this->writeSwitchStatement(s.as<SwitchStatement>(), out);
4847*c8dee2aaSAndroid Build Coastguard Worker             break;
4848*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kBreak:
4849*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpBranch, fBreakTarget.back(), out);
4850*c8dee2aaSAndroid Build Coastguard Worker             break;
4851*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kContinue:
4852*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpBranch, fContinueTarget.back(), out);
4853*c8dee2aaSAndroid Build Coastguard Worker             break;
4854*c8dee2aaSAndroid Build Coastguard Worker         case Statement::Kind::kDiscard:
4855*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpKill, out);
4856*c8dee2aaSAndroid Build Coastguard Worker             break;
4857*c8dee2aaSAndroid Build Coastguard Worker         default:
4858*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAILF("unsupported statement: %s", s.description().c_str());
4859*c8dee2aaSAndroid Build Coastguard Worker             break;
4860*c8dee2aaSAndroid Build Coastguard Worker     }
4861*c8dee2aaSAndroid Build Coastguard Worker }
4862*c8dee2aaSAndroid Build Coastguard Worker 
writeBlock(const Block & b,OutputStream & out)4863*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeBlock(const Block& b, OutputStream& out) {
4864*c8dee2aaSAndroid Build Coastguard Worker     for (const std::unique_ptr<Statement>& stmt : b.children()) {
4865*c8dee2aaSAndroid Build Coastguard Worker         this->writeStatement(*stmt, out);
4866*c8dee2aaSAndroid Build Coastguard Worker     }
4867*c8dee2aaSAndroid Build Coastguard Worker }
4868*c8dee2aaSAndroid Build Coastguard Worker 
getConditionalOpCounts()4869*c8dee2aaSAndroid Build Coastguard Worker SPIRVCodeGenerator::ConditionalOpCounts SPIRVCodeGenerator::getConditionalOpCounts() {
4870*c8dee2aaSAndroid Build Coastguard Worker     return {fReachableOps.size(), fStoreOps.size()};
4871*c8dee2aaSAndroid Build Coastguard Worker }
4872*c8dee2aaSAndroid Build Coastguard Worker 
pruneConditionalOps(ConditionalOpCounts ops)4873*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::pruneConditionalOps(ConditionalOpCounts ops) {
4874*c8dee2aaSAndroid Build Coastguard Worker     // Remove ops which are no longer reachable.
4875*c8dee2aaSAndroid Build Coastguard Worker     while (fReachableOps.size() > ops.numReachableOps) {
4876*c8dee2aaSAndroid Build Coastguard Worker         SpvId prunableSpvId = fReachableOps.back();
4877*c8dee2aaSAndroid Build Coastguard Worker         const Instruction* prunableOp = fSpvIdCache.find(prunableSpvId);
4878*c8dee2aaSAndroid Build Coastguard Worker 
4879*c8dee2aaSAndroid Build Coastguard Worker         if (prunableOp) {
4880*c8dee2aaSAndroid Build Coastguard Worker             fOpCache.remove(*prunableOp);
4881*c8dee2aaSAndroid Build Coastguard Worker             fSpvIdCache.remove(prunableSpvId);
4882*c8dee2aaSAndroid Build Coastguard Worker         } else {
4883*c8dee2aaSAndroid Build Coastguard Worker             SkDEBUGFAIL("reachable-op list contains unrecognized SpvId");
4884*c8dee2aaSAndroid Build Coastguard Worker         }
4885*c8dee2aaSAndroid Build Coastguard Worker 
4886*c8dee2aaSAndroid Build Coastguard Worker         fReachableOps.pop_back();
4887*c8dee2aaSAndroid Build Coastguard Worker     }
4888*c8dee2aaSAndroid Build Coastguard Worker 
4889*c8dee2aaSAndroid Build Coastguard Worker     // Remove any cached stores that occurred during the conditional block.
4890*c8dee2aaSAndroid Build Coastguard Worker     while (fStoreOps.size() > ops.numStoreOps) {
4891*c8dee2aaSAndroid Build Coastguard Worker         if (fStoreCache.find(fStoreOps.back())) {
4892*c8dee2aaSAndroid Build Coastguard Worker             fStoreCache.remove(fStoreOps.back());
4893*c8dee2aaSAndroid Build Coastguard Worker         }
4894*c8dee2aaSAndroid Build Coastguard Worker         fStoreOps.pop_back();
4895*c8dee2aaSAndroid Build Coastguard Worker     }
4896*c8dee2aaSAndroid Build Coastguard Worker }
4897*c8dee2aaSAndroid Build Coastguard Worker 
writeIfStatement(const IfStatement & stmt,OutputStream & out)4898*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeIfStatement(const IfStatement& stmt, OutputStream& out) {
4899*c8dee2aaSAndroid Build Coastguard Worker     SpvId test = this->writeExpression(*stmt.test(), out);
4900*c8dee2aaSAndroid Build Coastguard Worker     SpvId ifTrue = this->nextId(nullptr);
4901*c8dee2aaSAndroid Build Coastguard Worker     SpvId ifFalse = this->nextId(nullptr);
4902*c8dee2aaSAndroid Build Coastguard Worker 
4903*c8dee2aaSAndroid Build Coastguard Worker     ConditionalOpCounts conditionalOps = this->getConditionalOpCounts();
4904*c8dee2aaSAndroid Build Coastguard Worker 
4905*c8dee2aaSAndroid Build Coastguard Worker     if (stmt.ifFalse()) {
4906*c8dee2aaSAndroid Build Coastguard Worker         SpvId end = this->nextId(nullptr);
4907*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
4908*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpBranchConditional, test, ifTrue, ifFalse, out);
4909*c8dee2aaSAndroid Build Coastguard Worker         this->writeLabel(ifTrue, kBranchIsOnPreviousLine, out);
4910*c8dee2aaSAndroid Build Coastguard Worker         this->writeStatement(*stmt.ifTrue(), out);
4911*c8dee2aaSAndroid Build Coastguard Worker         if (fCurrentBlock) {
4912*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpBranch, end, out);
4913*c8dee2aaSAndroid Build Coastguard Worker         }
4914*c8dee2aaSAndroid Build Coastguard Worker         this->writeLabel(ifFalse, kBranchIsAbove, conditionalOps, out);
4915*c8dee2aaSAndroid Build Coastguard Worker         this->writeStatement(*stmt.ifFalse(), out);
4916*c8dee2aaSAndroid Build Coastguard Worker         if (fCurrentBlock) {
4917*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpBranch, end, out);
4918*c8dee2aaSAndroid Build Coastguard Worker         }
4919*c8dee2aaSAndroid Build Coastguard Worker         this->writeLabel(end, kBranchIsAbove, conditionalOps, out);
4920*c8dee2aaSAndroid Build Coastguard Worker     } else {
4921*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpSelectionMerge, ifFalse, SpvSelectionControlMaskNone, out);
4922*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpBranchConditional, test, ifTrue, ifFalse, out);
4923*c8dee2aaSAndroid Build Coastguard Worker         this->writeLabel(ifTrue, kBranchIsOnPreviousLine, out);
4924*c8dee2aaSAndroid Build Coastguard Worker         this->writeStatement(*stmt.ifTrue(), out);
4925*c8dee2aaSAndroid Build Coastguard Worker         if (fCurrentBlock) {
4926*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpBranch, ifFalse, out);
4927*c8dee2aaSAndroid Build Coastguard Worker         }
4928*c8dee2aaSAndroid Build Coastguard Worker         this->writeLabel(ifFalse, kBranchIsAbove, conditionalOps, out);
4929*c8dee2aaSAndroid Build Coastguard Worker     }
4930*c8dee2aaSAndroid Build Coastguard Worker }
4931*c8dee2aaSAndroid Build Coastguard Worker 
writeForStatement(const ForStatement & f,OutputStream & out)4932*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeForStatement(const ForStatement& f, OutputStream& out) {
4933*c8dee2aaSAndroid Build Coastguard Worker     if (f.initializer()) {
4934*c8dee2aaSAndroid Build Coastguard Worker         this->writeStatement(*f.initializer(), out);
4935*c8dee2aaSAndroid Build Coastguard Worker     }
4936*c8dee2aaSAndroid Build Coastguard Worker 
4937*c8dee2aaSAndroid Build Coastguard Worker     ConditionalOpCounts conditionalOps = this->getConditionalOpCounts();
4938*c8dee2aaSAndroid Build Coastguard Worker 
4939*c8dee2aaSAndroid Build Coastguard Worker     // The store cache isn't trustworthy in the presence of branches; store caching only makes sense
4940*c8dee2aaSAndroid Build Coastguard Worker     // in the context of linear straight-line execution. If we wanted to be more clever, we could
4941*c8dee2aaSAndroid Build Coastguard Worker     // only invalidate store cache entries for variables affected by the loop body, but for now we
4942*c8dee2aaSAndroid Build Coastguard Worker     // simply clear the entire cache whenever branching occurs.
4943*c8dee2aaSAndroid Build Coastguard Worker     SpvId header = this->nextId(nullptr);
4944*c8dee2aaSAndroid Build Coastguard Worker     SpvId start = this->nextId(nullptr);
4945*c8dee2aaSAndroid Build Coastguard Worker     SpvId body = this->nextId(nullptr);
4946*c8dee2aaSAndroid Build Coastguard Worker     SpvId next = this->nextId(nullptr);
4947*c8dee2aaSAndroid Build Coastguard Worker     fContinueTarget.push_back(next);
4948*c8dee2aaSAndroid Build Coastguard Worker     SpvId end = this->nextId(nullptr);
4949*c8dee2aaSAndroid Build Coastguard Worker     fBreakTarget.push_back(end);
4950*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranch, header, out);
4951*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(header, kBranchIsBelow, conditionalOps, out);
4952*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpLoopMerge, end, next, SpvLoopControlMaskNone, out);
4953*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranch, start, out);
4954*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(start, kBranchIsOnPreviousLine, out);
4955*c8dee2aaSAndroid Build Coastguard Worker     if (f.test()) {
4956*c8dee2aaSAndroid Build Coastguard Worker         SpvId test = this->writeExpression(*f.test(), out);
4957*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpBranchConditional, test, body, end, out);
4958*c8dee2aaSAndroid Build Coastguard Worker     } else {
4959*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpBranch, body, out);
4960*c8dee2aaSAndroid Build Coastguard Worker     }
4961*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(body, kBranchIsOnPreviousLine, out);
4962*c8dee2aaSAndroid Build Coastguard Worker     this->writeStatement(*f.statement(), out);
4963*c8dee2aaSAndroid Build Coastguard Worker     if (fCurrentBlock) {
4964*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpBranch, next, out);
4965*c8dee2aaSAndroid Build Coastguard Worker     }
4966*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(next, kBranchIsAbove, conditionalOps, out);
4967*c8dee2aaSAndroid Build Coastguard Worker     if (f.next()) {
4968*c8dee2aaSAndroid Build Coastguard Worker         this->writeExpression(*f.next(), out);
4969*c8dee2aaSAndroid Build Coastguard Worker     }
4970*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranch, header, out);
4971*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(end, kBranchIsAbove, conditionalOps, out);
4972*c8dee2aaSAndroid Build Coastguard Worker     fBreakTarget.pop_back();
4973*c8dee2aaSAndroid Build Coastguard Worker     fContinueTarget.pop_back();
4974*c8dee2aaSAndroid Build Coastguard Worker }
4975*c8dee2aaSAndroid Build Coastguard Worker 
writeDoStatement(const DoStatement & d,OutputStream & out)4976*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeDoStatement(const DoStatement& d, OutputStream& out) {
4977*c8dee2aaSAndroid Build Coastguard Worker     ConditionalOpCounts conditionalOps = this->getConditionalOpCounts();
4978*c8dee2aaSAndroid Build Coastguard Worker 
4979*c8dee2aaSAndroid Build Coastguard Worker     // The store cache isn't trustworthy in the presence of branches; store caching only makes sense
4980*c8dee2aaSAndroid Build Coastguard Worker     // in the context of linear straight-line execution. If we wanted to be more clever, we could
4981*c8dee2aaSAndroid Build Coastguard Worker     // only invalidate store cache entries for variables affected by the loop body, but for now we
4982*c8dee2aaSAndroid Build Coastguard Worker     // simply clear the entire cache whenever branching occurs.
4983*c8dee2aaSAndroid Build Coastguard Worker     SpvId header = this->nextId(nullptr);
4984*c8dee2aaSAndroid Build Coastguard Worker     SpvId start = this->nextId(nullptr);
4985*c8dee2aaSAndroid Build Coastguard Worker     SpvId next = this->nextId(nullptr);
4986*c8dee2aaSAndroid Build Coastguard Worker     SpvId continueTarget = this->nextId(nullptr);
4987*c8dee2aaSAndroid Build Coastguard Worker     fContinueTarget.push_back(continueTarget);
4988*c8dee2aaSAndroid Build Coastguard Worker     SpvId end = this->nextId(nullptr);
4989*c8dee2aaSAndroid Build Coastguard Worker     fBreakTarget.push_back(end);
4990*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranch, header, out);
4991*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(header, kBranchIsBelow, conditionalOps, out);
4992*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpLoopMerge, end, continueTarget, SpvLoopControlMaskNone, out);
4993*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranch, start, out);
4994*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(start, kBranchIsOnPreviousLine, out);
4995*c8dee2aaSAndroid Build Coastguard Worker     this->writeStatement(*d.statement(), out);
4996*c8dee2aaSAndroid Build Coastguard Worker     if (fCurrentBlock) {
4997*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpBranch, next, out);
4998*c8dee2aaSAndroid Build Coastguard Worker         this->writeLabel(next, kBranchIsOnPreviousLine, out);
4999*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpBranch, continueTarget, out);
5000*c8dee2aaSAndroid Build Coastguard Worker     }
5001*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(continueTarget, kBranchIsAbove, conditionalOps, out);
5002*c8dee2aaSAndroid Build Coastguard Worker     SpvId test = this->writeExpression(*d.test(), out);
5003*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpBranchConditional, test, header, end, out);
5004*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(end, kBranchIsAbove, conditionalOps, out);
5005*c8dee2aaSAndroid Build Coastguard Worker     fBreakTarget.pop_back();
5006*c8dee2aaSAndroid Build Coastguard Worker     fContinueTarget.pop_back();
5007*c8dee2aaSAndroid Build Coastguard Worker }
5008*c8dee2aaSAndroid Build Coastguard Worker 
writeSwitchStatement(const SwitchStatement & s,OutputStream & out)5009*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeSwitchStatement(const SwitchStatement& s, OutputStream& out) {
5010*c8dee2aaSAndroid Build Coastguard Worker     SpvId value = this->writeExpression(*s.value(), out);
5011*c8dee2aaSAndroid Build Coastguard Worker 
5012*c8dee2aaSAndroid Build Coastguard Worker     ConditionalOpCounts conditionalOps = this->getConditionalOpCounts();
5013*c8dee2aaSAndroid Build Coastguard Worker 
5014*c8dee2aaSAndroid Build Coastguard Worker     // The store cache isn't trustworthy in the presence of branches; store caching only makes sense
5015*c8dee2aaSAndroid Build Coastguard Worker     // in the context of linear straight-line execution. If we wanted to be more clever, we could
5016*c8dee2aaSAndroid Build Coastguard Worker     // only invalidate store cache entries for variables affected by the switch body, but for now we
5017*c8dee2aaSAndroid Build Coastguard Worker     // simply clear the entire cache whenever branching occurs.
5018*c8dee2aaSAndroid Build Coastguard Worker     TArray<SpvId> labels;
5019*c8dee2aaSAndroid Build Coastguard Worker     SpvId end = this->nextId(nullptr);
5020*c8dee2aaSAndroid Build Coastguard Worker     SpvId defaultLabel = end;
5021*c8dee2aaSAndroid Build Coastguard Worker     fBreakTarget.push_back(end);
5022*c8dee2aaSAndroid Build Coastguard Worker     int size = 3;
5023*c8dee2aaSAndroid Build Coastguard Worker     const StatementArray& cases = s.cases();
5024*c8dee2aaSAndroid Build Coastguard Worker     for (const std::unique_ptr<Statement>& stmt : cases) {
5025*c8dee2aaSAndroid Build Coastguard Worker         const SwitchCase& c = stmt->as<SwitchCase>();
5026*c8dee2aaSAndroid Build Coastguard Worker         SpvId label = this->nextId(nullptr);
5027*c8dee2aaSAndroid Build Coastguard Worker         labels.push_back(label);
5028*c8dee2aaSAndroid Build Coastguard Worker         if (!c.isDefault()) {
5029*c8dee2aaSAndroid Build Coastguard Worker             size += 2;
5030*c8dee2aaSAndroid Build Coastguard Worker         } else {
5031*c8dee2aaSAndroid Build Coastguard Worker             defaultLabel = label;
5032*c8dee2aaSAndroid Build Coastguard Worker         }
5033*c8dee2aaSAndroid Build Coastguard Worker     }
5034*c8dee2aaSAndroid Build Coastguard Worker 
5035*c8dee2aaSAndroid Build Coastguard Worker     // We should have exactly one label for each case.
5036*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(labels.size() == cases.size());
5037*c8dee2aaSAndroid Build Coastguard Worker 
5038*c8dee2aaSAndroid Build Coastguard Worker     // Collapse adjacent switch-cases into one; that is, reduce `case 1: case 2: case 3:` into a
5039*c8dee2aaSAndroid Build Coastguard Worker     // single OpLabel. The Tint SPIR-V reader does not support switch-case fallthrough, but it
5040*c8dee2aaSAndroid Build Coastguard Worker     // does support multiple switch-cases branching to the same label.
5041*c8dee2aaSAndroid Build Coastguard Worker     SkBitSet caseIsCollapsed(cases.size());
5042*c8dee2aaSAndroid Build Coastguard Worker     for (int index = cases.size() - 2; index >= 0; index--) {
5043*c8dee2aaSAndroid Build Coastguard Worker         if (cases[index]->as<SwitchCase>().statement()->isEmpty()) {
5044*c8dee2aaSAndroid Build Coastguard Worker             caseIsCollapsed.set(index);
5045*c8dee2aaSAndroid Build Coastguard Worker             labels[index] = labels[index + 1];
5046*c8dee2aaSAndroid Build Coastguard Worker         }
5047*c8dee2aaSAndroid Build Coastguard Worker     }
5048*c8dee2aaSAndroid Build Coastguard Worker 
5049*c8dee2aaSAndroid Build Coastguard Worker     labels.push_back(end);
5050*c8dee2aaSAndroid Build Coastguard Worker 
5051*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpSelectionMerge, end, SpvSelectionControlMaskNone, out);
5052*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(SpvOpSwitch, size, out);
5053*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(value, out);
5054*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(defaultLabel, out);
5055*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < cases.size(); ++i) {
5056*c8dee2aaSAndroid Build Coastguard Worker         const SwitchCase& c = cases[i]->as<SwitchCase>();
5057*c8dee2aaSAndroid Build Coastguard Worker         if (c.isDefault()) {
5058*c8dee2aaSAndroid Build Coastguard Worker             continue;
5059*c8dee2aaSAndroid Build Coastguard Worker         }
5060*c8dee2aaSAndroid Build Coastguard Worker         this->writeWord(c.value(), out);
5061*c8dee2aaSAndroid Build Coastguard Worker         this->writeWord(labels[i], out);
5062*c8dee2aaSAndroid Build Coastguard Worker     }
5063*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < cases.size(); ++i) {
5064*c8dee2aaSAndroid Build Coastguard Worker         if (caseIsCollapsed.test(i)) {
5065*c8dee2aaSAndroid Build Coastguard Worker             continue;
5066*c8dee2aaSAndroid Build Coastguard Worker         }
5067*c8dee2aaSAndroid Build Coastguard Worker         const SwitchCase& c = cases[i]->as<SwitchCase>();
5068*c8dee2aaSAndroid Build Coastguard Worker         if (i == 0) {
5069*c8dee2aaSAndroid Build Coastguard Worker             this->writeLabel(labels[i], kBranchIsOnPreviousLine, out);
5070*c8dee2aaSAndroid Build Coastguard Worker         } else {
5071*c8dee2aaSAndroid Build Coastguard Worker             this->writeLabel(labels[i], kBranchIsAbove, conditionalOps, out);
5072*c8dee2aaSAndroid Build Coastguard Worker         }
5073*c8dee2aaSAndroid Build Coastguard Worker         this->writeStatement(*c.statement(), out);
5074*c8dee2aaSAndroid Build Coastguard Worker         if (fCurrentBlock) {
5075*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpBranch, labels[i + 1], out);
5076*c8dee2aaSAndroid Build Coastguard Worker         }
5077*c8dee2aaSAndroid Build Coastguard Worker     }
5078*c8dee2aaSAndroid Build Coastguard Worker     this->writeLabel(end, kBranchIsAbove, conditionalOps, out);
5079*c8dee2aaSAndroid Build Coastguard Worker     fBreakTarget.pop_back();
5080*c8dee2aaSAndroid Build Coastguard Worker }
5081*c8dee2aaSAndroid Build Coastguard Worker 
writeReturnStatement(const ReturnStatement & r,OutputStream & out)5082*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeReturnStatement(const ReturnStatement& r, OutputStream& out) {
5083*c8dee2aaSAndroid Build Coastguard Worker     if (r.expression()) {
5084*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpReturnValue, this->writeExpression(*r.expression(), out),
5085*c8dee2aaSAndroid Build Coastguard Worker                                out);
5086*c8dee2aaSAndroid Build Coastguard Worker     } else {
5087*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpReturn, out);
5088*c8dee2aaSAndroid Build Coastguard Worker     }
5089*c8dee2aaSAndroid Build Coastguard Worker }
5090*c8dee2aaSAndroid Build Coastguard Worker 
5091*c8dee2aaSAndroid Build Coastguard Worker // Given any function, returns the top-level symbol table (OUTSIDE of the function's scope).
get_top_level_symbol_table(const FunctionDeclaration & anyFunc)5092*c8dee2aaSAndroid Build Coastguard Worker static SymbolTable* get_top_level_symbol_table(const FunctionDeclaration& anyFunc) {
5093*c8dee2aaSAndroid Build Coastguard Worker     return anyFunc.definition()->body()->as<Block>().symbolTable()->fParent;
5094*c8dee2aaSAndroid Build Coastguard Worker }
5095*c8dee2aaSAndroid Build Coastguard Worker 
writeEntrypointAdapter(const FunctionDeclaration & main)5096*c8dee2aaSAndroid Build Coastguard Worker SPIRVCodeGenerator::EntrypointAdapter SPIRVCodeGenerator::writeEntrypointAdapter(
5097*c8dee2aaSAndroid Build Coastguard Worker         const FunctionDeclaration& main) {
5098*c8dee2aaSAndroid Build Coastguard Worker     // Our goal is to synthesize a tiny helper function which looks like this:
5099*c8dee2aaSAndroid Build Coastguard Worker     //     void _entrypoint() { sk_FragColor = main(); }
5100*c8dee2aaSAndroid Build Coastguard Worker 
5101*c8dee2aaSAndroid Build Coastguard Worker     // Fish a symbol table out of main().
5102*c8dee2aaSAndroid Build Coastguard Worker     SymbolTable* symbolTable = get_top_level_symbol_table(main);
5103*c8dee2aaSAndroid Build Coastguard Worker 
5104*c8dee2aaSAndroid Build Coastguard Worker     // Get `sk_FragColor` as a writable reference.
5105*c8dee2aaSAndroid Build Coastguard Worker     const Symbol* skFragColorSymbol = symbolTable->find("sk_FragColor");
5106*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(skFragColorSymbol);
5107*c8dee2aaSAndroid Build Coastguard Worker     const Variable& skFragColorVar = skFragColorSymbol->as<Variable>();
5108*c8dee2aaSAndroid Build Coastguard Worker     auto skFragColorRef = std::make_unique<VariableReference>(Position(), &skFragColorVar,
5109*c8dee2aaSAndroid Build Coastguard Worker                                                               VariableReference::RefKind::kWrite);
5110*c8dee2aaSAndroid Build Coastguard Worker 
5111*c8dee2aaSAndroid Build Coastguard Worker     // TODO get secondary frag color as one as well?
5112*c8dee2aaSAndroid Build Coastguard Worker 
5113*c8dee2aaSAndroid Build Coastguard Worker     // Synthesize a call to the `main()` function.
5114*c8dee2aaSAndroid Build Coastguard Worker     if (!main.returnType().matches(skFragColorRef->type())) {
5115*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(main.fPosition, "SPIR-V does not support returning '" +
5116*c8dee2aaSAndroid Build Coastguard Worker                 main.returnType().description() + "' from main()");
5117*c8dee2aaSAndroid Build Coastguard Worker         return {};
5118*c8dee2aaSAndroid Build Coastguard Worker     }
5119*c8dee2aaSAndroid Build Coastguard Worker     ExpressionArray args;
5120*c8dee2aaSAndroid Build Coastguard Worker     if (main.parameters().size() == 1) {
5121*c8dee2aaSAndroid Build Coastguard Worker         if (!main.parameters()[0]->type().matches(*fContext.fTypes.fFloat2)) {
5122*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(main.fPosition,
5123*c8dee2aaSAndroid Build Coastguard Worker                     "SPIR-V does not support parameter of type '" +
5124*c8dee2aaSAndroid Build Coastguard Worker                     main.parameters()[0]->type().description() + "' to main()");
5125*c8dee2aaSAndroid Build Coastguard Worker             return {};
5126*c8dee2aaSAndroid Build Coastguard Worker         }
5127*c8dee2aaSAndroid Build Coastguard Worker         double kZero[2] = {0.0, 0.0};
5128*c8dee2aaSAndroid Build Coastguard Worker         args.push_back(ConstructorCompound::MakeFromConstants(fContext, Position{},
5129*c8dee2aaSAndroid Build Coastguard Worker                                                               *fContext.fTypes.fFloat2, kZero));
5130*c8dee2aaSAndroid Build Coastguard Worker     }
5131*c8dee2aaSAndroid Build Coastguard Worker     auto callMainFn = FunctionCall::Make(fContext, Position(), &main.returnType(),
5132*c8dee2aaSAndroid Build Coastguard Worker                                          main, std::move(args));
5133*c8dee2aaSAndroid Build Coastguard Worker 
5134*c8dee2aaSAndroid Build Coastguard Worker     // Synthesize `skFragColor = main()` as a BinaryExpression.
5135*c8dee2aaSAndroid Build Coastguard Worker     auto assignmentStmt = std::make_unique<ExpressionStatement>(std::make_unique<BinaryExpression>(
5136*c8dee2aaSAndroid Build Coastguard Worker             Position(),
5137*c8dee2aaSAndroid Build Coastguard Worker             std::move(skFragColorRef),
5138*c8dee2aaSAndroid Build Coastguard Worker             Operator::Kind::EQ,
5139*c8dee2aaSAndroid Build Coastguard Worker             std::move(callMainFn),
5140*c8dee2aaSAndroid Build Coastguard Worker             &main.returnType()));
5141*c8dee2aaSAndroid Build Coastguard Worker 
5142*c8dee2aaSAndroid Build Coastguard Worker     // Function bodies are always wrapped in a Block.
5143*c8dee2aaSAndroid Build Coastguard Worker     StatementArray entrypointStmts;
5144*c8dee2aaSAndroid Build Coastguard Worker     entrypointStmts.push_back(std::move(assignmentStmt));
5145*c8dee2aaSAndroid Build Coastguard Worker     auto entrypointBlock = Block::Make(Position(), std::move(entrypointStmts),
5146*c8dee2aaSAndroid Build Coastguard Worker                                        Block::Kind::kBracedScope, /*symbols=*/nullptr);
5147*c8dee2aaSAndroid Build Coastguard Worker     // Declare an entrypoint function.
5148*c8dee2aaSAndroid Build Coastguard Worker     EntrypointAdapter adapter;
5149*c8dee2aaSAndroid Build Coastguard Worker     adapter.entrypointDecl =
5150*c8dee2aaSAndroid Build Coastguard Worker             std::make_unique<FunctionDeclaration>(fContext,
5151*c8dee2aaSAndroid Build Coastguard Worker                                                   Position(),
5152*c8dee2aaSAndroid Build Coastguard Worker                                                   ModifierFlag::kNone,
5153*c8dee2aaSAndroid Build Coastguard Worker                                                   "_entrypoint",
5154*c8dee2aaSAndroid Build Coastguard Worker                                                   /*parameters=*/TArray<Variable*>{},
5155*c8dee2aaSAndroid Build Coastguard Worker                                                   /*returnType=*/fContext.fTypes.fVoid.get(),
5156*c8dee2aaSAndroid Build Coastguard Worker                                                   kNotIntrinsic);
5157*c8dee2aaSAndroid Build Coastguard Worker     // Define it.
5158*c8dee2aaSAndroid Build Coastguard Worker     adapter.entrypointDef = FunctionDefinition::Convert(fContext,
5159*c8dee2aaSAndroid Build Coastguard Worker                                                         Position(),
5160*c8dee2aaSAndroid Build Coastguard Worker                                                         *adapter.entrypointDecl,
5161*c8dee2aaSAndroid Build Coastguard Worker                                                         std::move(entrypointBlock));
5162*c8dee2aaSAndroid Build Coastguard Worker 
5163*c8dee2aaSAndroid Build Coastguard Worker     adapter.entrypointDecl->setDefinition(adapter.entrypointDef.get());
5164*c8dee2aaSAndroid Build Coastguard Worker     return adapter;
5165*c8dee2aaSAndroid Build Coastguard Worker }
5166*c8dee2aaSAndroid Build Coastguard Worker 
writeUniformBuffer(SymbolTable * topLevelSymbolTable)5167*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeUniformBuffer(SymbolTable* topLevelSymbolTable) {
5168*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fTopLevelUniforms.empty());
5169*c8dee2aaSAndroid Build Coastguard Worker     static constexpr char kUniformBufferName[] = "_UniformBuffer";
5170*c8dee2aaSAndroid Build Coastguard Worker 
5171*c8dee2aaSAndroid Build Coastguard Worker     // Convert the list of top-level uniforms into a matching struct named _UniformBuffer, and build
5172*c8dee2aaSAndroid Build Coastguard Worker     // a lookup table of variables to UniformBuffer field indices.
5173*c8dee2aaSAndroid Build Coastguard Worker     TArray<Field> fields;
5174*c8dee2aaSAndroid Build Coastguard Worker     fields.reserve_exact(fTopLevelUniforms.size());
5175*c8dee2aaSAndroid Build Coastguard Worker     for (const VarDeclaration* topLevelUniform : fTopLevelUniforms) {
5176*c8dee2aaSAndroid Build Coastguard Worker         const Variable* var = topLevelUniform->var();
5177*c8dee2aaSAndroid Build Coastguard Worker         fTopLevelUniformMap.set(var, (int)fields.size());
5178*c8dee2aaSAndroid Build Coastguard Worker         ModifierFlags flags = var->modifierFlags() & ~ModifierFlag::kUniform;
5179*c8dee2aaSAndroid Build Coastguard Worker         fields.emplace_back(var->fPosition, var->layout(), flags, var->name(), &var->type());
5180*c8dee2aaSAndroid Build Coastguard Worker     }
5181*c8dee2aaSAndroid Build Coastguard Worker     fUniformBuffer.fStruct = Type::MakeStructType(fContext,
5182*c8dee2aaSAndroid Build Coastguard Worker                                                   Position(),
5183*c8dee2aaSAndroid Build Coastguard Worker                                                   kUniformBufferName,
5184*c8dee2aaSAndroid Build Coastguard Worker                                                   std::move(fields),
5185*c8dee2aaSAndroid Build Coastguard Worker                                                   /*interfaceBlock=*/true);
5186*c8dee2aaSAndroid Build Coastguard Worker 
5187*c8dee2aaSAndroid Build Coastguard Worker     // Create a global variable to contain this struct.
5188*c8dee2aaSAndroid Build Coastguard Worker     Layout layout;
5189*c8dee2aaSAndroid Build Coastguard Worker     layout.fBinding = fProgram.fConfig->fSettings.fDefaultUniformBinding;
5190*c8dee2aaSAndroid Build Coastguard Worker     layout.fSet     = fProgram.fConfig->fSettings.fDefaultUniformSet;
5191*c8dee2aaSAndroid Build Coastguard Worker 
5192*c8dee2aaSAndroid Build Coastguard Worker     fUniformBuffer.fInnerVariable = Variable::Make(/*pos=*/Position(),
5193*c8dee2aaSAndroid Build Coastguard Worker                                                    /*modifiersPosition=*/Position(),
5194*c8dee2aaSAndroid Build Coastguard Worker                                                    layout,
5195*c8dee2aaSAndroid Build Coastguard Worker                                                    ModifierFlag::kUniform,
5196*c8dee2aaSAndroid Build Coastguard Worker                                                    fUniformBuffer.fStruct.get(),
5197*c8dee2aaSAndroid Build Coastguard Worker                                                    kUniformBufferName,
5198*c8dee2aaSAndroid Build Coastguard Worker                                                    /*mangledName=*/"",
5199*c8dee2aaSAndroid Build Coastguard Worker                                                    /*builtin=*/false,
5200*c8dee2aaSAndroid Build Coastguard Worker                                                    Variable::Storage::kGlobal);
5201*c8dee2aaSAndroid Build Coastguard Worker 
5202*c8dee2aaSAndroid Build Coastguard Worker     // Create an interface block object for this global variable.
5203*c8dee2aaSAndroid Build Coastguard Worker     fUniformBuffer.fInterfaceBlock =
5204*c8dee2aaSAndroid Build Coastguard Worker             std::make_unique<InterfaceBlock>(Position(), fUniformBuffer.fInnerVariable.get());
5205*c8dee2aaSAndroid Build Coastguard Worker 
5206*c8dee2aaSAndroid Build Coastguard Worker     // Generate an interface block and hold onto its ID.
5207*c8dee2aaSAndroid Build Coastguard Worker     fUniformBufferId = this->writeInterfaceBlock(*fUniformBuffer.fInterfaceBlock);
5208*c8dee2aaSAndroid Build Coastguard Worker }
5209*c8dee2aaSAndroid Build Coastguard Worker 
addRTFlipUniform(Position pos)5210*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::addRTFlipUniform(Position pos) {
5211*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fProgram.fConfig->fSettings.fForceNoRTFlip);
5212*c8dee2aaSAndroid Build Coastguard Worker 
5213*c8dee2aaSAndroid Build Coastguard Worker     if (fWroteRTFlip) {
5214*c8dee2aaSAndroid Build Coastguard Worker         return;
5215*c8dee2aaSAndroid Build Coastguard Worker     }
5216*c8dee2aaSAndroid Build Coastguard Worker     // Flip variable hasn't been written yet. This means we don't have an existing
5217*c8dee2aaSAndroid Build Coastguard Worker     // interface block, so we're free to just synthesize one.
5218*c8dee2aaSAndroid Build Coastguard Worker     fWroteRTFlip = true;
5219*c8dee2aaSAndroid Build Coastguard Worker     TArray<Field> fields;
5220*c8dee2aaSAndroid Build Coastguard Worker     if (fProgram.fConfig->fSettings.fRTFlipOffset < 0) {
5221*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(pos, "RTFlipOffset is negative");
5222*c8dee2aaSAndroid Build Coastguard Worker     }
5223*c8dee2aaSAndroid Build Coastguard Worker     fields.emplace_back(pos,
5224*c8dee2aaSAndroid Build Coastguard Worker                         Layout(LayoutFlag::kNone,
5225*c8dee2aaSAndroid Build Coastguard Worker                                /*location=*/-1,
5226*c8dee2aaSAndroid Build Coastguard Worker                                fProgram.fConfig->fSettings.fRTFlipOffset,
5227*c8dee2aaSAndroid Build Coastguard Worker                                /*binding=*/-1,
5228*c8dee2aaSAndroid Build Coastguard Worker                                /*index=*/-1,
5229*c8dee2aaSAndroid Build Coastguard Worker                                /*set=*/-1,
5230*c8dee2aaSAndroid Build Coastguard Worker                                /*builtin=*/-1,
5231*c8dee2aaSAndroid Build Coastguard Worker                                /*inputAttachmentIndex=*/-1),
5232*c8dee2aaSAndroid Build Coastguard Worker                         ModifierFlag::kNone,
5233*c8dee2aaSAndroid Build Coastguard Worker                         SKSL_RTFLIP_NAME,
5234*c8dee2aaSAndroid Build Coastguard Worker                         fContext.fTypes.fFloat2.get());
5235*c8dee2aaSAndroid Build Coastguard Worker     std::string_view name = "sksl_synthetic_uniforms";
5236*c8dee2aaSAndroid Build Coastguard Worker     const Type* intfStruct = fSynthetics.takeOwnershipOfSymbol(Type::MakeStructType(
5237*c8dee2aaSAndroid Build Coastguard Worker             fContext, Position(), name, std::move(fields), /*interfaceBlock=*/true));
5238*c8dee2aaSAndroid Build Coastguard Worker     bool usePushConstants = fProgram.fConfig->fSettings.fUsePushConstants;
5239*c8dee2aaSAndroid Build Coastguard Worker     int binding = -1, set = -1;
5240*c8dee2aaSAndroid Build Coastguard Worker     if (!usePushConstants) {
5241*c8dee2aaSAndroid Build Coastguard Worker         binding = fProgram.fConfig->fSettings.fRTFlipBinding;
5242*c8dee2aaSAndroid Build Coastguard Worker         if (binding == -1) {
5243*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(pos, "layout(binding=...) is required in SPIR-V");
5244*c8dee2aaSAndroid Build Coastguard Worker         }
5245*c8dee2aaSAndroid Build Coastguard Worker         set = fProgram.fConfig->fSettings.fRTFlipSet;
5246*c8dee2aaSAndroid Build Coastguard Worker         if (set == -1) {
5247*c8dee2aaSAndroid Build Coastguard Worker             fContext.fErrors->error(pos, "layout(set=...) is required in SPIR-V");
5248*c8dee2aaSAndroid Build Coastguard Worker         }
5249*c8dee2aaSAndroid Build Coastguard Worker     }
5250*c8dee2aaSAndroid Build Coastguard Worker     Layout layout(/*flags=*/usePushConstants ? LayoutFlag::kPushConstant : LayoutFlag::kNone,
5251*c8dee2aaSAndroid Build Coastguard Worker                   /*location=*/-1,
5252*c8dee2aaSAndroid Build Coastguard Worker                   /*offset=*/-1,
5253*c8dee2aaSAndroid Build Coastguard Worker                   binding,
5254*c8dee2aaSAndroid Build Coastguard Worker                   /*index=*/-1,
5255*c8dee2aaSAndroid Build Coastguard Worker                   set,
5256*c8dee2aaSAndroid Build Coastguard Worker                   /*builtin=*/-1,
5257*c8dee2aaSAndroid Build Coastguard Worker                   /*inputAttachmentIndex=*/-1);
5258*c8dee2aaSAndroid Build Coastguard Worker     Variable* intfVar =
5259*c8dee2aaSAndroid Build Coastguard Worker             fSynthetics.takeOwnershipOfSymbol(Variable::Make(/*pos=*/Position(),
5260*c8dee2aaSAndroid Build Coastguard Worker                                                              /*modifiersPosition=*/Position(),
5261*c8dee2aaSAndroid Build Coastguard Worker                                                              layout,
5262*c8dee2aaSAndroid Build Coastguard Worker                                                              ModifierFlag::kUniform,
5263*c8dee2aaSAndroid Build Coastguard Worker                                                              intfStruct,
5264*c8dee2aaSAndroid Build Coastguard Worker                                                              name,
5265*c8dee2aaSAndroid Build Coastguard Worker                                                              /*mangledName=*/"",
5266*c8dee2aaSAndroid Build Coastguard Worker                                                              /*builtin=*/false,
5267*c8dee2aaSAndroid Build Coastguard Worker                                                              Variable::Storage::kGlobal));
5268*c8dee2aaSAndroid Build Coastguard Worker     {
5269*c8dee2aaSAndroid Build Coastguard Worker         AutoAttachPoolToThread attach(fProgram.fPool.get());
5270*c8dee2aaSAndroid Build Coastguard Worker         fProgram.fSymbols->add(fContext,
5271*c8dee2aaSAndroid Build Coastguard Worker                                std::make_unique<FieldSymbol>(Position(), intfVar, /*field=*/0));
5272*c8dee2aaSAndroid Build Coastguard Worker     }
5273*c8dee2aaSAndroid Build Coastguard Worker     InterfaceBlock intf(Position(), intfVar);
5274*c8dee2aaSAndroid Build Coastguard Worker     this->writeInterfaceBlock(intf, false);
5275*c8dee2aaSAndroid Build Coastguard Worker }
5276*c8dee2aaSAndroid Build Coastguard Worker 
synthesizeTextureAndSampler(const Variable & combinedSampler)5277*c8dee2aaSAndroid Build Coastguard Worker std::tuple<const Variable*, const Variable*> SPIRVCodeGenerator::synthesizeTextureAndSampler(
5278*c8dee2aaSAndroid Build Coastguard Worker         const Variable& combinedSampler) {
5279*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(fUseTextureSamplerPairs);
5280*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(combinedSampler.type().typeKind() == Type::TypeKind::kSampler);
5281*c8dee2aaSAndroid Build Coastguard Worker 
5282*c8dee2aaSAndroid Build Coastguard Worker     if (std::unique_ptr<SynthesizedTextureSamplerPair>* existingData =
5283*c8dee2aaSAndroid Build Coastguard Worker             fSynthesizedSamplerMap.find(&combinedSampler)) {
5284*c8dee2aaSAndroid Build Coastguard Worker         return {(*existingData)->fTexture.get(), (*existingData)->fSampler.get()};
5285*c8dee2aaSAndroid Build Coastguard Worker     }
5286*c8dee2aaSAndroid Build Coastguard Worker 
5287*c8dee2aaSAndroid Build Coastguard Worker     auto data = std::make_unique<SynthesizedTextureSamplerPair>();
5288*c8dee2aaSAndroid Build Coastguard Worker 
5289*c8dee2aaSAndroid Build Coastguard Worker     Layout texLayout = combinedSampler.layout();
5290*c8dee2aaSAndroid Build Coastguard Worker     texLayout.fBinding = texLayout.fTexture;
5291*c8dee2aaSAndroid Build Coastguard Worker     data->fTextureName = std::string(combinedSampler.name()) + "_texture";
5292*c8dee2aaSAndroid Build Coastguard Worker 
5293*c8dee2aaSAndroid Build Coastguard Worker     auto texture = Variable::Make(/*pos=*/Position(),
5294*c8dee2aaSAndroid Build Coastguard Worker                                   /*modifiersPosition=*/Position(),
5295*c8dee2aaSAndroid Build Coastguard Worker                                   texLayout,
5296*c8dee2aaSAndroid Build Coastguard Worker                                   combinedSampler.modifierFlags(),
5297*c8dee2aaSAndroid Build Coastguard Worker                                   &combinedSampler.type().textureType(),
5298*c8dee2aaSAndroid Build Coastguard Worker                                   data->fTextureName,
5299*c8dee2aaSAndroid Build Coastguard Worker                                   /*mangledName=*/"",
5300*c8dee2aaSAndroid Build Coastguard Worker                                   /*builtin=*/false,
5301*c8dee2aaSAndroid Build Coastguard Worker                                   Variable::Storage::kGlobal);
5302*c8dee2aaSAndroid Build Coastguard Worker 
5303*c8dee2aaSAndroid Build Coastguard Worker     Layout samplerLayout = combinedSampler.layout();
5304*c8dee2aaSAndroid Build Coastguard Worker     samplerLayout.fBinding = samplerLayout.fSampler;
5305*c8dee2aaSAndroid Build Coastguard Worker     samplerLayout.fFlags &= ~LayoutFlag::kAllPixelFormats;
5306*c8dee2aaSAndroid Build Coastguard Worker     data->fSamplerName = std::string(combinedSampler.name()) + "_sampler";
5307*c8dee2aaSAndroid Build Coastguard Worker 
5308*c8dee2aaSAndroid Build Coastguard Worker     auto sampler = Variable::Make(/*pos=*/Position(),
5309*c8dee2aaSAndroid Build Coastguard Worker                                   /*modifiersPosition=*/Position(),
5310*c8dee2aaSAndroid Build Coastguard Worker                                   samplerLayout,
5311*c8dee2aaSAndroid Build Coastguard Worker                                   combinedSampler.modifierFlags(),
5312*c8dee2aaSAndroid Build Coastguard Worker                                   fContext.fTypes.fSampler.get(),
5313*c8dee2aaSAndroid Build Coastguard Worker                                   data->fSamplerName,
5314*c8dee2aaSAndroid Build Coastguard Worker                                   /*mangledName=*/"",
5315*c8dee2aaSAndroid Build Coastguard Worker                                   /*builtin=*/false,
5316*c8dee2aaSAndroid Build Coastguard Worker                                   Variable::Storage::kGlobal);
5317*c8dee2aaSAndroid Build Coastguard Worker 
5318*c8dee2aaSAndroid Build Coastguard Worker     const Variable* t = texture.get();
5319*c8dee2aaSAndroid Build Coastguard Worker     const Variable* s = sampler.get();
5320*c8dee2aaSAndroid Build Coastguard Worker     data->fTexture = std::move(texture);
5321*c8dee2aaSAndroid Build Coastguard Worker     data->fSampler = std::move(sampler);
5322*c8dee2aaSAndroid Build Coastguard Worker     fSynthesizedSamplerMap.set(&combinedSampler, std::move(data));
5323*c8dee2aaSAndroid Build Coastguard Worker 
5324*c8dee2aaSAndroid Build Coastguard Worker     return {t, s};
5325*c8dee2aaSAndroid Build Coastguard Worker }
5326*c8dee2aaSAndroid Build Coastguard Worker 
writeInstructions(const Program & program,OutputStream & out)5327*c8dee2aaSAndroid Build Coastguard Worker void SPIRVCodeGenerator::writeInstructions(const Program& program, OutputStream& out) {
5328*c8dee2aaSAndroid Build Coastguard Worker     Analysis::FindFunctionsToSpecialize(program, &fSpecializationInfo, [](const Variable& param) {
5329*c8dee2aaSAndroid Build Coastguard Worker         return param.type().isSampler() || param.type().isUnsizedArray();
5330*c8dee2aaSAndroid Build Coastguard Worker     });
5331*c8dee2aaSAndroid Build Coastguard Worker 
5332*c8dee2aaSAndroid Build Coastguard Worker     fGLSLExtendedInstructions = this->nextId(nullptr);
5333*c8dee2aaSAndroid Build Coastguard Worker     StringStream body;
5334*c8dee2aaSAndroid Build Coastguard Worker 
5335*c8dee2aaSAndroid Build Coastguard Worker     // Do an initial pass over the program elements to establish some baseline info.
5336*c8dee2aaSAndroid Build Coastguard Worker     const FunctionDeclaration* main = nullptr;
5337*c8dee2aaSAndroid Build Coastguard Worker     int localSizeX = 1, localSizeY = 1, localSizeZ = 1;
5338*c8dee2aaSAndroid Build Coastguard Worker     Position combinedSamplerPos;
5339*c8dee2aaSAndroid Build Coastguard Worker     Position separateSamplerPos;
5340*c8dee2aaSAndroid Build Coastguard Worker     for (const ProgramElement* e : program.elements()) {
5341*c8dee2aaSAndroid Build Coastguard Worker         switch (e->kind()) {
5342*c8dee2aaSAndroid Build Coastguard Worker             case ProgramElement::Kind::kFunction: {
5343*c8dee2aaSAndroid Build Coastguard Worker                 // Assign SpvIds to functions.
5344*c8dee2aaSAndroid Build Coastguard Worker                 const FunctionDefinition& funcDef = e->as<FunctionDefinition>();
5345*c8dee2aaSAndroid Build Coastguard Worker                 const FunctionDeclaration& funcDecl = funcDef.declaration();
5346*c8dee2aaSAndroid Build Coastguard Worker                 if (const Analysis::Specializations* specializations =
5347*c8dee2aaSAndroid Build Coastguard Worker                             fSpecializationInfo.fSpecializationMap.find(&funcDecl)) {
5348*c8dee2aaSAndroid Build Coastguard Worker                     for (int i = 0; i < specializations->size(); i++) {
5349*c8dee2aaSAndroid Build Coastguard Worker                         fFunctionMap.set({&funcDecl, i}, this->nextId(nullptr));
5350*c8dee2aaSAndroid Build Coastguard Worker                     }
5351*c8dee2aaSAndroid Build Coastguard Worker                 } else {
5352*c8dee2aaSAndroid Build Coastguard Worker                     fFunctionMap.set({&funcDecl, Analysis::kUnspecialized}, this->nextId(nullptr));
5353*c8dee2aaSAndroid Build Coastguard Worker                 }
5354*c8dee2aaSAndroid Build Coastguard Worker                 if (funcDecl.isMain()) {
5355*c8dee2aaSAndroid Build Coastguard Worker                     main = &funcDecl;
5356*c8dee2aaSAndroid Build Coastguard Worker                 }
5357*c8dee2aaSAndroid Build Coastguard Worker                 break;
5358*c8dee2aaSAndroid Build Coastguard Worker             }
5359*c8dee2aaSAndroid Build Coastguard Worker             case ProgramElement::Kind::kGlobalVar: {
5360*c8dee2aaSAndroid Build Coastguard Worker                 // Look for sampler variables and determine whether or not this program uses
5361*c8dee2aaSAndroid Build Coastguard Worker                 // combined samplers or separate samplers. The layout backend will be marked as
5362*c8dee2aaSAndroid Build Coastguard Worker                 // WebGPU for separate samplers, or Vulkan for combined samplers.
5363*c8dee2aaSAndroid Build Coastguard Worker                 const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
5364*c8dee2aaSAndroid Build Coastguard Worker                 const Variable& var = *decl.varDeclaration().var();
5365*c8dee2aaSAndroid Build Coastguard Worker                 if (var.type().isSampler()) {
5366*c8dee2aaSAndroid Build Coastguard Worker                     if (var.layout().fFlags & LayoutFlag::kVulkan) {
5367*c8dee2aaSAndroid Build Coastguard Worker                         combinedSamplerPos = decl.position();
5368*c8dee2aaSAndroid Build Coastguard Worker                     }
5369*c8dee2aaSAndroid Build Coastguard Worker                     if (var.layout().fFlags & (LayoutFlag::kWebGPU | LayoutFlag::kDirect3D)) {
5370*c8dee2aaSAndroid Build Coastguard Worker                         separateSamplerPos = decl.position();
5371*c8dee2aaSAndroid Build Coastguard Worker                     }
5372*c8dee2aaSAndroid Build Coastguard Worker                 }
5373*c8dee2aaSAndroid Build Coastguard Worker                 break;
5374*c8dee2aaSAndroid Build Coastguard Worker             }
5375*c8dee2aaSAndroid Build Coastguard Worker             case ProgramElement::Kind::kModifiers: {
5376*c8dee2aaSAndroid Build Coastguard Worker                 // If this is a compute program, collect the local-size values. Dimensions that are
5377*c8dee2aaSAndroid Build Coastguard Worker                 // not present will be assigned a value of 1.
5378*c8dee2aaSAndroid Build Coastguard Worker                 if (ProgramConfig::IsCompute(program.fConfig->fKind)) {
5379*c8dee2aaSAndroid Build Coastguard Worker                     const ModifiersDeclaration& modifiers = e->as<ModifiersDeclaration>();
5380*c8dee2aaSAndroid Build Coastguard Worker                     if (modifiers.layout().fLocalSizeX >= 0) {
5381*c8dee2aaSAndroid Build Coastguard Worker                         localSizeX = modifiers.layout().fLocalSizeX;
5382*c8dee2aaSAndroid Build Coastguard Worker                     }
5383*c8dee2aaSAndroid Build Coastguard Worker                     if (modifiers.layout().fLocalSizeY >= 0) {
5384*c8dee2aaSAndroid Build Coastguard Worker                         localSizeY = modifiers.layout().fLocalSizeY;
5385*c8dee2aaSAndroid Build Coastguard Worker                     }
5386*c8dee2aaSAndroid Build Coastguard Worker                     if (modifiers.layout().fLocalSizeZ >= 0) {
5387*c8dee2aaSAndroid Build Coastguard Worker                         localSizeZ = modifiers.layout().fLocalSizeZ;
5388*c8dee2aaSAndroid Build Coastguard Worker                     }
5389*c8dee2aaSAndroid Build Coastguard Worker                 }
5390*c8dee2aaSAndroid Build Coastguard Worker                 break;
5391*c8dee2aaSAndroid Build Coastguard Worker             }
5392*c8dee2aaSAndroid Build Coastguard Worker             default:
5393*c8dee2aaSAndroid Build Coastguard Worker                 break;
5394*c8dee2aaSAndroid Build Coastguard Worker         }
5395*c8dee2aaSAndroid Build Coastguard Worker     }
5396*c8dee2aaSAndroid Build Coastguard Worker 
5397*c8dee2aaSAndroid Build Coastguard Worker     // Make sure we have a main() function.
5398*c8dee2aaSAndroid Build Coastguard Worker     if (!main) {
5399*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(Position(), "program does not contain a main() function");
5400*c8dee2aaSAndroid Build Coastguard Worker         return;
5401*c8dee2aaSAndroid Build Coastguard Worker     }
5402*c8dee2aaSAndroid Build Coastguard Worker     // Make sure our program's sampler usage is consistent.
5403*c8dee2aaSAndroid Build Coastguard Worker     if (combinedSamplerPos.valid() && separateSamplerPos.valid()) {
5404*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(Position(), "programs cannot contain a mixture of sampler types");
5405*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(combinedSamplerPos, "combined sampler found here:");
5406*c8dee2aaSAndroid Build Coastguard Worker         fContext.fErrors->error(separateSamplerPos, "separate sampler found here:");
5407*c8dee2aaSAndroid Build Coastguard Worker         return;
5408*c8dee2aaSAndroid Build Coastguard Worker     }
5409*c8dee2aaSAndroid Build Coastguard Worker     fUseTextureSamplerPairs = separateSamplerPos.valid();
5410*c8dee2aaSAndroid Build Coastguard Worker 
5411*c8dee2aaSAndroid Build Coastguard Worker     // Emit interface blocks.
5412*c8dee2aaSAndroid Build Coastguard Worker     std::set<SpvId> interfaceVars;
5413*c8dee2aaSAndroid Build Coastguard Worker     for (const ProgramElement* e : program.elements()) {
5414*c8dee2aaSAndroid Build Coastguard Worker         if (e->is<InterfaceBlock>()) {
5415*c8dee2aaSAndroid Build Coastguard Worker             const InterfaceBlock& intf = e->as<InterfaceBlock>();
5416*c8dee2aaSAndroid Build Coastguard Worker             SpvId id = this->writeInterfaceBlock(intf);
5417*c8dee2aaSAndroid Build Coastguard Worker 
5418*c8dee2aaSAndroid Build Coastguard Worker             if ((intf.var()->modifierFlags() & (ModifierFlag::kIn | ModifierFlag::kOut)) &&
5419*c8dee2aaSAndroid Build Coastguard Worker                 intf.var()->layout().fBuiltin == -1) {
5420*c8dee2aaSAndroid Build Coastguard Worker                 interfaceVars.insert(id);
5421*c8dee2aaSAndroid Build Coastguard Worker             }
5422*c8dee2aaSAndroid Build Coastguard Worker         }
5423*c8dee2aaSAndroid Build Coastguard Worker     }
5424*c8dee2aaSAndroid Build Coastguard Worker     // If MustDeclareFragmentFrontFacing is set, the front-facing flag (sk_Clockwise) needs to be
5425*c8dee2aaSAndroid Build Coastguard Worker     // explicitly declared in the output, whether or not the program explicitly references it.
5426*c8dee2aaSAndroid Build Coastguard Worker     // However, if the program naturally declares it, we don't want to include it a second time;
5427*c8dee2aaSAndroid Build Coastguard Worker     // we keep track of the real global variable declarations to see if sk_Clockwise is emitted.
5428*c8dee2aaSAndroid Build Coastguard Worker     const VarDeclaration* missingClockwiseDecl = nullptr;
5429*c8dee2aaSAndroid Build Coastguard Worker     if (fCaps.fMustDeclareFragmentFrontFacing) {
5430*c8dee2aaSAndroid Build Coastguard Worker         if (const Symbol* clockwise = program.fSymbols->findBuiltinSymbol("sk_Clockwise")) {
5431*c8dee2aaSAndroid Build Coastguard Worker             missingClockwiseDecl = clockwise->as<Variable>().varDeclaration();
5432*c8dee2aaSAndroid Build Coastguard Worker         }
5433*c8dee2aaSAndroid Build Coastguard Worker     }
5434*c8dee2aaSAndroid Build Coastguard Worker     // Emit global variable declarations.
5435*c8dee2aaSAndroid Build Coastguard Worker     for (const ProgramElement* e : program.elements()) {
5436*c8dee2aaSAndroid Build Coastguard Worker         if (e->is<GlobalVarDeclaration>()) {
5437*c8dee2aaSAndroid Build Coastguard Worker             const VarDeclaration& decl = e->as<GlobalVarDeclaration>().varDeclaration();
5438*c8dee2aaSAndroid Build Coastguard Worker             if (!this->writeGlobalVarDeclaration(program.fConfig->fKind, decl)) {
5439*c8dee2aaSAndroid Build Coastguard Worker                 return;
5440*c8dee2aaSAndroid Build Coastguard Worker             }
5441*c8dee2aaSAndroid Build Coastguard Worker             if (missingClockwiseDecl == &decl) {
5442*c8dee2aaSAndroid Build Coastguard Worker                 // We emitted an sk_Clockwise declaration naturally, so we don't need a workaround.
5443*c8dee2aaSAndroid Build Coastguard Worker                 missingClockwiseDecl = nullptr;
5444*c8dee2aaSAndroid Build Coastguard Worker             }
5445*c8dee2aaSAndroid Build Coastguard Worker         }
5446*c8dee2aaSAndroid Build Coastguard Worker     }
5447*c8dee2aaSAndroid Build Coastguard Worker     // All the global variables have been declared. If sk_Clockwise was not naturally included in
5448*c8dee2aaSAndroid Build Coastguard Worker     // the output, but MustDeclareFragmentFrontFacing was set, we need to bodge it in ourselves.
5449*c8dee2aaSAndroid Build Coastguard Worker     if (missingClockwiseDecl) {
5450*c8dee2aaSAndroid Build Coastguard Worker         if (!this->writeGlobalVarDeclaration(program.fConfig->fKind, *missingClockwiseDecl)) {
5451*c8dee2aaSAndroid Build Coastguard Worker             return;
5452*c8dee2aaSAndroid Build Coastguard Worker         }
5453*c8dee2aaSAndroid Build Coastguard Worker         missingClockwiseDecl = nullptr;
5454*c8dee2aaSAndroid Build Coastguard Worker     }
5455*c8dee2aaSAndroid Build Coastguard Worker     // Emit top-level uniforms into a dedicated uniform buffer.
5456*c8dee2aaSAndroid Build Coastguard Worker     if (!fTopLevelUniforms.empty()) {
5457*c8dee2aaSAndroid Build Coastguard Worker         this->writeUniformBuffer(get_top_level_symbol_table(*main));
5458*c8dee2aaSAndroid Build Coastguard Worker     }
5459*c8dee2aaSAndroid Build Coastguard Worker     // If main() returns a half4, synthesize a tiny entrypoint function which invokes the real
5460*c8dee2aaSAndroid Build Coastguard Worker     // main() and stores the result into sk_FragColor.
5461*c8dee2aaSAndroid Build Coastguard Worker     EntrypointAdapter adapter;
5462*c8dee2aaSAndroid Build Coastguard Worker     if (main->returnType().matches(*fContext.fTypes.fHalf4)) {
5463*c8dee2aaSAndroid Build Coastguard Worker         adapter = this->writeEntrypointAdapter(*main);
5464*c8dee2aaSAndroid Build Coastguard Worker         if (adapter.entrypointDecl) {
5465*c8dee2aaSAndroid Build Coastguard Worker             fFunctionMap.set({adapter.entrypointDecl.get(), Analysis::kUnspecialized},
5466*c8dee2aaSAndroid Build Coastguard Worker                              this->nextId(nullptr));
5467*c8dee2aaSAndroid Build Coastguard Worker             this->writeFunction(*adapter.entrypointDef, body);
5468*c8dee2aaSAndroid Build Coastguard Worker             main = adapter.entrypointDecl.get();
5469*c8dee2aaSAndroid Build Coastguard Worker         }
5470*c8dee2aaSAndroid Build Coastguard Worker     }
5471*c8dee2aaSAndroid Build Coastguard Worker     // Emit all the functions.
5472*c8dee2aaSAndroid Build Coastguard Worker     for (const ProgramElement* e : program.elements()) {
5473*c8dee2aaSAndroid Build Coastguard Worker         if (e->is<FunctionDefinition>()) {
5474*c8dee2aaSAndroid Build Coastguard Worker             this->writeFunction(e->as<FunctionDefinition>(), body);
5475*c8dee2aaSAndroid Build Coastguard Worker         }
5476*c8dee2aaSAndroid Build Coastguard Worker     }
5477*c8dee2aaSAndroid Build Coastguard Worker     // Add global in/out variables to the list of interface variables.
5478*c8dee2aaSAndroid Build Coastguard Worker     for (const auto& [var, spvId] : fVariableMap) {
5479*c8dee2aaSAndroid Build Coastguard Worker         if (var->storage() == Variable::Storage::kGlobal &&
5480*c8dee2aaSAndroid Build Coastguard Worker             (var->modifierFlags() & (ModifierFlag::kIn | ModifierFlag::kOut))) {
5481*c8dee2aaSAndroid Build Coastguard Worker             interfaceVars.insert(spvId);
5482*c8dee2aaSAndroid Build Coastguard Worker         }
5483*c8dee2aaSAndroid Build Coastguard Worker     }
5484*c8dee2aaSAndroid Build Coastguard Worker     this->writeCapabilities(out);
5485*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpExtInstImport, fGLSLExtendedInstructions, "GLSL.std.450", out);
5486*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstruction(SpvOpMemoryModel, SpvAddressingModelLogical, SpvMemoryModelGLSL450, out);
5487*c8dee2aaSAndroid Build Coastguard Worker     this->writeOpCode(SpvOpEntryPoint,
5488*c8dee2aaSAndroid Build Coastguard Worker                       (SpvId)(3 + (main->name().length() + 4) / 4) + (int32_t)interfaceVars.size(),
5489*c8dee2aaSAndroid Build Coastguard Worker                       out);
5490*c8dee2aaSAndroid Build Coastguard Worker     if (ProgramConfig::IsVertex(program.fConfig->fKind)) {
5491*c8dee2aaSAndroid Build Coastguard Worker         this->writeWord(SpvExecutionModelVertex, out);
5492*c8dee2aaSAndroid Build Coastguard Worker     } else if (ProgramConfig::IsFragment(program.fConfig->fKind)) {
5493*c8dee2aaSAndroid Build Coastguard Worker         this->writeWord(SpvExecutionModelFragment, out);
5494*c8dee2aaSAndroid Build Coastguard Worker     } else if (ProgramConfig::IsCompute(program.fConfig->fKind)) {
5495*c8dee2aaSAndroid Build Coastguard Worker         this->writeWord(SpvExecutionModelGLCompute, out);
5496*c8dee2aaSAndroid Build Coastguard Worker     } else {
5497*c8dee2aaSAndroid Build Coastguard Worker         SK_ABORT("cannot write this kind of program to SPIR-V\n");
5498*c8dee2aaSAndroid Build Coastguard Worker     }
5499*c8dee2aaSAndroid Build Coastguard Worker     const Analysis::SpecializedFunctionKey mainKey{main, Analysis::kUnspecialized};
5500*c8dee2aaSAndroid Build Coastguard Worker     SpvId entryPoint = fFunctionMap[mainKey];
5501*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(entryPoint, out);
5502*c8dee2aaSAndroid Build Coastguard Worker     this->writeString(main->name(), out);
5503*c8dee2aaSAndroid Build Coastguard Worker     for (int var : interfaceVars) {
5504*c8dee2aaSAndroid Build Coastguard Worker         this->writeWord(var, out);
5505*c8dee2aaSAndroid Build Coastguard Worker     }
5506*c8dee2aaSAndroid Build Coastguard Worker     if (ProgramConfig::IsFragment(program.fConfig->fKind)) {
5507*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpExecutionMode,
5508*c8dee2aaSAndroid Build Coastguard Worker                                fFunctionMap[mainKey],
5509*c8dee2aaSAndroid Build Coastguard Worker                                SpvExecutionModeOriginUpperLeft,
5510*c8dee2aaSAndroid Build Coastguard Worker                                out);
5511*c8dee2aaSAndroid Build Coastguard Worker     } else if (ProgramConfig::IsCompute(program.fConfig->fKind)) {
5512*c8dee2aaSAndroid Build Coastguard Worker         this->writeInstruction(SpvOpExecutionMode,
5513*c8dee2aaSAndroid Build Coastguard Worker                                fFunctionMap[mainKey],
5514*c8dee2aaSAndroid Build Coastguard Worker                                SpvExecutionModeLocalSize,
5515*c8dee2aaSAndroid Build Coastguard Worker                                localSizeX, localSizeY, localSizeZ,
5516*c8dee2aaSAndroid Build Coastguard Worker                                out);
5517*c8dee2aaSAndroid Build Coastguard Worker     }
5518*c8dee2aaSAndroid Build Coastguard Worker     for (const ProgramElement* e : program.elements()) {
5519*c8dee2aaSAndroid Build Coastguard Worker         if (e->is<Extension>()) {
5520*c8dee2aaSAndroid Build Coastguard Worker             this->writeInstruction(SpvOpSourceExtension, e->as<Extension>().name(), out);
5521*c8dee2aaSAndroid Build Coastguard Worker         }
5522*c8dee2aaSAndroid Build Coastguard Worker     }
5523*c8dee2aaSAndroid Build Coastguard Worker 
5524*c8dee2aaSAndroid Build Coastguard Worker     write_stringstream(fNameBuffer, out);
5525*c8dee2aaSAndroid Build Coastguard Worker     write_stringstream(fDecorationBuffer, out);
5526*c8dee2aaSAndroid Build Coastguard Worker     write_stringstream(fConstantBuffer, out);
5527*c8dee2aaSAndroid Build Coastguard Worker     write_stringstream(body, out);
5528*c8dee2aaSAndroid Build Coastguard Worker }
5529*c8dee2aaSAndroid Build Coastguard Worker 
generateCode()5530*c8dee2aaSAndroid Build Coastguard Worker bool SPIRVCodeGenerator::generateCode() {
5531*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(!fContext.fErrors->errorCount());
5532*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(SpvMagicNumber, *fOut);
5533*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(SpvVersion, *fOut);
5534*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(SKSL_MAGIC, *fOut);
5535*c8dee2aaSAndroid Build Coastguard Worker     StringStream buffer;
5536*c8dee2aaSAndroid Build Coastguard Worker     this->writeInstructions(fProgram, buffer);
5537*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(fIdCount, *fOut);
5538*c8dee2aaSAndroid Build Coastguard Worker     this->writeWord(0, *fOut); // reserved, always zero
5539*c8dee2aaSAndroid Build Coastguard Worker     write_stringstream(buffer, *fOut);
5540*c8dee2aaSAndroid Build Coastguard Worker     return fContext.fErrors->errorCount() == 0;
5541*c8dee2aaSAndroid Build Coastguard Worker }
5542*c8dee2aaSAndroid Build Coastguard Worker 
ToSPIRV(Program & program,const ShaderCaps * caps,OutputStream & out,ValidateSPIRVProc validateSPIRV)5543*c8dee2aaSAndroid Build Coastguard Worker bool ToSPIRV(Program& program,
5544*c8dee2aaSAndroid Build Coastguard Worker              const ShaderCaps* caps,
5545*c8dee2aaSAndroid Build Coastguard Worker              OutputStream& out,
5546*c8dee2aaSAndroid Build Coastguard Worker              ValidateSPIRVProc validateSPIRV) {
5547*c8dee2aaSAndroid Build Coastguard Worker     TRACE_EVENT0("skia.shaders", "SkSL::ToSPIRV");
5548*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(caps != nullptr);
5549*c8dee2aaSAndroid Build Coastguard Worker 
5550*c8dee2aaSAndroid Build Coastguard Worker     program.fContext->fErrors->setSource(*program.fSource);
5551*c8dee2aaSAndroid Build Coastguard Worker     bool result;
5552*c8dee2aaSAndroid Build Coastguard Worker     if (validateSPIRV) {
5553*c8dee2aaSAndroid Build Coastguard Worker         StringStream buffer;
5554*c8dee2aaSAndroid Build Coastguard Worker         SPIRVCodeGenerator cg(program.fContext.get(), caps, &program, &buffer);
5555*c8dee2aaSAndroid Build Coastguard Worker         result = cg.generateCode();
5556*c8dee2aaSAndroid Build Coastguard Worker 
5557*c8dee2aaSAndroid Build Coastguard Worker         if (result && program.fConfig->fSettings.fValidateSPIRV) {
5558*c8dee2aaSAndroid Build Coastguard Worker             std::string_view binary = buffer.str();
5559*c8dee2aaSAndroid Build Coastguard Worker             result = validateSPIRV(*program.fContext->fErrors, binary);
5560*c8dee2aaSAndroid Build Coastguard Worker             out.write(binary.data(), binary.size());
5561*c8dee2aaSAndroid Build Coastguard Worker         }
5562*c8dee2aaSAndroid Build Coastguard Worker     } else {
5563*c8dee2aaSAndroid Build Coastguard Worker         SPIRVCodeGenerator cg(program.fContext.get(), caps, &program, &out);
5564*c8dee2aaSAndroid Build Coastguard Worker         result = cg.generateCode();
5565*c8dee2aaSAndroid Build Coastguard Worker     }
5566*c8dee2aaSAndroid Build Coastguard Worker     program.fContext->fErrors->setSource(std::string_view());
5567*c8dee2aaSAndroid Build Coastguard Worker 
5568*c8dee2aaSAndroid Build Coastguard Worker     return result;
5569*c8dee2aaSAndroid Build Coastguard Worker }
5570*c8dee2aaSAndroid Build Coastguard Worker 
ToSPIRV(Program & program,const ShaderCaps * caps,std::string * out,ValidateSPIRVProc validateSPIRV)5571*c8dee2aaSAndroid Build Coastguard Worker bool ToSPIRV(Program& program,
5572*c8dee2aaSAndroid Build Coastguard Worker              const ShaderCaps* caps,
5573*c8dee2aaSAndroid Build Coastguard Worker              std::string* out,
5574*c8dee2aaSAndroid Build Coastguard Worker              ValidateSPIRVProc validateSPIRV) {
5575*c8dee2aaSAndroid Build Coastguard Worker     StringStream buffer;
5576*c8dee2aaSAndroid Build Coastguard Worker     if (!ToSPIRV(program, caps, buffer, validateSPIRV)) {
5577*c8dee2aaSAndroid Build Coastguard Worker         return false;
5578*c8dee2aaSAndroid Build Coastguard Worker     }
5579*c8dee2aaSAndroid Build Coastguard Worker     *out = buffer.str();
5580*c8dee2aaSAndroid Build Coastguard Worker     return true;
5581*c8dee2aaSAndroid Build Coastguard Worker }
5582*c8dee2aaSAndroid Build Coastguard Worker 
5583*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL
5584