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, ¤tColumn, &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, ¤tColumn, &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