xref: /aosp_15_r20/external/skia/src/sksl/codegen/SkSLRasterPipelineBuilder.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2022 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 #ifndef SKSL_RASTERPIPELINEBUILDER
9*c8dee2aaSAndroid Build Coastguard Worker #define SKSL_RASTERPIPELINEBUILDER
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
12*c8dee2aaSAndroid Build Coastguard Worker 
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkUtils.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterPipelineOpList.h"
18*c8dee2aaSAndroid Build Coastguard Worker 
19*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
20*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
21*c8dee2aaSAndroid Build Coastguard Worker #include <memory>
22*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
23*c8dee2aaSAndroid Build Coastguard Worker 
24*c8dee2aaSAndroid Build Coastguard Worker class SkArenaAlloc;
25*c8dee2aaSAndroid Build Coastguard Worker class SkRasterPipeline;
26*c8dee2aaSAndroid Build Coastguard Worker class SkWStream;
27*c8dee2aaSAndroid Build Coastguard Worker using SkRPOffset = uint32_t;
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker namespace SkSL {
30*c8dee2aaSAndroid Build Coastguard Worker 
31*c8dee2aaSAndroid Build Coastguard Worker class DebugTracePriv;
32*c8dee2aaSAndroid Build Coastguard Worker class TraceHook;
33*c8dee2aaSAndroid Build Coastguard Worker 
34*c8dee2aaSAndroid Build Coastguard Worker namespace RP {
35*c8dee2aaSAndroid Build Coastguard Worker 
36*c8dee2aaSAndroid Build Coastguard Worker // A single scalar in our program consumes one slot.
37*c8dee2aaSAndroid Build Coastguard Worker using Slot = int;
38*c8dee2aaSAndroid Build Coastguard Worker constexpr Slot NA = -1;
39*c8dee2aaSAndroid Build Coastguard Worker 
40*c8dee2aaSAndroid Build Coastguard Worker // Scalars, vectors, and matrices can be represented as a range of slot indices.
41*c8dee2aaSAndroid Build Coastguard Worker struct SlotRange {
42*c8dee2aaSAndroid Build Coastguard Worker     Slot index = 0;
43*c8dee2aaSAndroid Build Coastguard Worker     int count = 0;
44*c8dee2aaSAndroid Build Coastguard Worker };
45*c8dee2aaSAndroid Build Coastguard Worker 
46*c8dee2aaSAndroid Build Coastguard Worker #define SKRP_EXTENDED_OPS(M)     \
47*c8dee2aaSAndroid Build Coastguard Worker     /* branch targets */         \
48*c8dee2aaSAndroid Build Coastguard Worker     M(label)                     \
49*c8dee2aaSAndroid Build Coastguard Worker                                  \
50*c8dee2aaSAndroid Build Coastguard Worker     /* child programs */         \
51*c8dee2aaSAndroid Build Coastguard Worker     M(invoke_shader)             \
52*c8dee2aaSAndroid Build Coastguard Worker     M(invoke_color_filter)       \
53*c8dee2aaSAndroid Build Coastguard Worker     M(invoke_blender)            \
54*c8dee2aaSAndroid Build Coastguard Worker                                  \
55*c8dee2aaSAndroid Build Coastguard Worker     /* color space transforms */ \
56*c8dee2aaSAndroid Build Coastguard Worker     M(invoke_to_linear_srgb)     \
57*c8dee2aaSAndroid Build Coastguard Worker     M(invoke_from_linear_srgb)
58*c8dee2aaSAndroid Build Coastguard Worker 
59*c8dee2aaSAndroid Build Coastguard Worker // An RP::Program will consist entirely of ProgramOps. The ProgramOps list is a superset of the
60*c8dee2aaSAndroid Build Coastguard Worker // native SkRasterPipelineOps op-list. It also has a few extra ops to indicate child-effect
61*c8dee2aaSAndroid Build Coastguard Worker // invocation, and a `label` op to indicate branch targets.
62*c8dee2aaSAndroid Build Coastguard Worker enum class ProgramOp {
63*c8dee2aaSAndroid Build Coastguard Worker     #define M(stage) stage,
64*c8dee2aaSAndroid Build Coastguard Worker         // A finished program can contain any native Raster Pipeline op...
65*c8dee2aaSAndroid Build Coastguard Worker         SK_RASTER_PIPELINE_OPS_ALL(M)
66*c8dee2aaSAndroid Build Coastguard Worker 
67*c8dee2aaSAndroid Build Coastguard Worker         // ... as well as our extended ops.
68*c8dee2aaSAndroid Build Coastguard Worker         SKRP_EXTENDED_OPS(M)
69*c8dee2aaSAndroid Build Coastguard Worker     #undef M
70*c8dee2aaSAndroid Build Coastguard Worker };
71*c8dee2aaSAndroid Build Coastguard Worker 
72*c8dee2aaSAndroid Build Coastguard Worker // BuilderOps are a superset of ProgramOps. They are used by the RP::Builder, which works in terms
73*c8dee2aaSAndroid Build Coastguard Worker // of Instructions; Instructions are slightly more expressive than raw SkRasterPipelineOps. In
74*c8dee2aaSAndroid Build Coastguard Worker // particular, the Builder supports stacks for pushing and popping scratch values.
75*c8dee2aaSAndroid Build Coastguard Worker // RP::Program::makeStages is responsible for rewriting Instructions/BuilderOps into an array of
76*c8dee2aaSAndroid Build Coastguard Worker // RP::Program::Stages, which will contain only native SkRasterPipelineOps and (optionally)
77*c8dee2aaSAndroid Build Coastguard Worker // child-effect invocations.
78*c8dee2aaSAndroid Build Coastguard Worker enum class BuilderOp {
79*c8dee2aaSAndroid Build Coastguard Worker     #define M(stage) stage,
80*c8dee2aaSAndroid Build Coastguard Worker         // An in-flight program can contain all the native Raster Pipeline ops...
81*c8dee2aaSAndroid Build Coastguard Worker         SK_RASTER_PIPELINE_OPS_ALL(M)
82*c8dee2aaSAndroid Build Coastguard Worker 
83*c8dee2aaSAndroid Build Coastguard Worker         // ... and our extended ops...
84*c8dee2aaSAndroid Build Coastguard Worker         SKRP_EXTENDED_OPS(M)
85*c8dee2aaSAndroid Build Coastguard Worker     #undef M
86*c8dee2aaSAndroid Build Coastguard Worker 
87*c8dee2aaSAndroid Build Coastguard Worker     // ... and also has Builder-specific ops. These ops generally interface with the stack, and are
88*c8dee2aaSAndroid Build Coastguard Worker     // converted into ProgramOps during `makeStages`.
89*c8dee2aaSAndroid Build Coastguard Worker     push_clone,
90*c8dee2aaSAndroid Build Coastguard Worker     push_clone_from_stack,
91*c8dee2aaSAndroid Build Coastguard Worker     push_clone_indirect_from_stack,
92*c8dee2aaSAndroid Build Coastguard Worker     push_constant,
93*c8dee2aaSAndroid Build Coastguard Worker     push_immutable,
94*c8dee2aaSAndroid Build Coastguard Worker     push_immutable_indirect,
95*c8dee2aaSAndroid Build Coastguard Worker     push_slots,
96*c8dee2aaSAndroid Build Coastguard Worker     push_slots_indirect,
97*c8dee2aaSAndroid Build Coastguard Worker     push_uniform,
98*c8dee2aaSAndroid Build Coastguard Worker     push_uniform_indirect,
99*c8dee2aaSAndroid Build Coastguard Worker     copy_stack_to_slots,
100*c8dee2aaSAndroid Build Coastguard Worker     copy_stack_to_slots_unmasked,
101*c8dee2aaSAndroid Build Coastguard Worker     copy_stack_to_slots_indirect,
102*c8dee2aaSAndroid Build Coastguard Worker     copy_uniform_to_slots_unmasked,
103*c8dee2aaSAndroid Build Coastguard Worker     store_immutable_value,
104*c8dee2aaSAndroid Build Coastguard Worker     swizzle_copy_stack_to_slots,
105*c8dee2aaSAndroid Build Coastguard Worker     swizzle_copy_stack_to_slots_indirect,
106*c8dee2aaSAndroid Build Coastguard Worker     discard_stack,
107*c8dee2aaSAndroid Build Coastguard Worker     pad_stack,
108*c8dee2aaSAndroid Build Coastguard Worker     select,
109*c8dee2aaSAndroid Build Coastguard Worker     push_condition_mask,
110*c8dee2aaSAndroid Build Coastguard Worker     pop_condition_mask,
111*c8dee2aaSAndroid Build Coastguard Worker     push_loop_mask,
112*c8dee2aaSAndroid Build Coastguard Worker     pop_loop_mask,
113*c8dee2aaSAndroid Build Coastguard Worker     pop_and_reenable_loop_mask,
114*c8dee2aaSAndroid Build Coastguard Worker     push_return_mask,
115*c8dee2aaSAndroid Build Coastguard Worker     pop_return_mask,
116*c8dee2aaSAndroid Build Coastguard Worker     push_src_rgba,
117*c8dee2aaSAndroid Build Coastguard Worker     push_dst_rgba,
118*c8dee2aaSAndroid Build Coastguard Worker     push_device_xy01,
119*c8dee2aaSAndroid Build Coastguard Worker     pop_src_rgba,
120*c8dee2aaSAndroid Build Coastguard Worker     pop_dst_rgba,
121*c8dee2aaSAndroid Build Coastguard Worker     trace_var_indirect,
122*c8dee2aaSAndroid Build Coastguard Worker     branch_if_no_active_lanes_on_stack_top_equal,
123*c8dee2aaSAndroid Build Coastguard Worker     unsupported
124*c8dee2aaSAndroid Build Coastguard Worker };
125*c8dee2aaSAndroid Build Coastguard Worker 
126*c8dee2aaSAndroid Build Coastguard Worker // If the extended ops are not in sync between enums, program creation will not work.
127*c8dee2aaSAndroid Build Coastguard Worker static_assert((int)ProgramOp::label == (int)BuilderOp::label);
128*c8dee2aaSAndroid Build Coastguard Worker 
129*c8dee2aaSAndroid Build Coastguard Worker // Represents a single raster-pipeline SkSL instruction.
130*c8dee2aaSAndroid Build Coastguard Worker struct Instruction {
131*c8dee2aaSAndroid Build Coastguard Worker     BuilderOp fOp;
132*c8dee2aaSAndroid Build Coastguard Worker     Slot      fSlotA = NA;
133*c8dee2aaSAndroid Build Coastguard Worker     Slot      fSlotB = NA;
134*c8dee2aaSAndroid Build Coastguard Worker     int       fImmA = 0;
135*c8dee2aaSAndroid Build Coastguard Worker     int       fImmB = 0;
136*c8dee2aaSAndroid Build Coastguard Worker     int       fImmC = 0;
137*c8dee2aaSAndroid Build Coastguard Worker     int       fImmD = 0;
138*c8dee2aaSAndroid Build Coastguard Worker     int       fStackID = 0;
139*c8dee2aaSAndroid Build Coastguard Worker };
140*c8dee2aaSAndroid Build Coastguard Worker 
141*c8dee2aaSAndroid Build Coastguard Worker class Callbacks {
142*c8dee2aaSAndroid Build Coastguard Worker public:
143*c8dee2aaSAndroid Build Coastguard Worker     virtual ~Callbacks() = default;
144*c8dee2aaSAndroid Build Coastguard Worker 
145*c8dee2aaSAndroid Build Coastguard Worker     virtual bool appendShader(int index) = 0;
146*c8dee2aaSAndroid Build Coastguard Worker     virtual bool appendColorFilter(int index) = 0;
147*c8dee2aaSAndroid Build Coastguard Worker     virtual bool appendBlender(int index) = 0;
148*c8dee2aaSAndroid Build Coastguard Worker 
149*c8dee2aaSAndroid Build Coastguard Worker     virtual void toLinearSrgb(const void* color) = 0;
150*c8dee2aaSAndroid Build Coastguard Worker     virtual void fromLinearSrgb(const void* color) = 0;
151*c8dee2aaSAndroid Build Coastguard Worker };
152*c8dee2aaSAndroid Build Coastguard Worker 
153*c8dee2aaSAndroid Build Coastguard Worker class Program {
154*c8dee2aaSAndroid Build Coastguard Worker public:
155*c8dee2aaSAndroid Build Coastguard Worker     Program(skia_private::TArray<Instruction> instrs,
156*c8dee2aaSAndroid Build Coastguard Worker             int numValueSlots,
157*c8dee2aaSAndroid Build Coastguard Worker             int numUniformSlots,
158*c8dee2aaSAndroid Build Coastguard Worker             int numImmutableSlots,
159*c8dee2aaSAndroid Build Coastguard Worker             int numLabels,
160*c8dee2aaSAndroid Build Coastguard Worker             DebugTracePriv* debugTrace);
161*c8dee2aaSAndroid Build Coastguard Worker     ~Program();
162*c8dee2aaSAndroid Build Coastguard Worker 
163*c8dee2aaSAndroid Build Coastguard Worker     bool appendStages(SkRasterPipeline* pipeline,
164*c8dee2aaSAndroid Build Coastguard Worker                       SkArenaAlloc* alloc,
165*c8dee2aaSAndroid Build Coastguard Worker                       Callbacks* callbacks,
166*c8dee2aaSAndroid Build Coastguard Worker                       SkSpan<const float> uniforms) const;
167*c8dee2aaSAndroid Build Coastguard Worker 
168*c8dee2aaSAndroid Build Coastguard Worker     void dump(SkWStream* out, bool writeInstructionCount = false) const;
169*c8dee2aaSAndroid Build Coastguard Worker 
numUniforms()170*c8dee2aaSAndroid Build Coastguard Worker     int numUniforms() const { return fNumUniformSlots; }
171*c8dee2aaSAndroid Build Coastguard Worker 
172*c8dee2aaSAndroid Build Coastguard Worker private:
173*c8dee2aaSAndroid Build Coastguard Worker     using StackDepths = skia_private::TArray<int>; // [stack index] = depth of stack
174*c8dee2aaSAndroid Build Coastguard Worker 
175*c8dee2aaSAndroid Build Coastguard Worker     struct SlotData {
176*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<float> values;
177*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<float> stack;
178*c8dee2aaSAndroid Build Coastguard Worker         SkSpan<float> immutable;
179*c8dee2aaSAndroid Build Coastguard Worker     };
180*c8dee2aaSAndroid Build Coastguard Worker     std::optional<SlotData> allocateSlotData(SkArenaAlloc* alloc) const;
181*c8dee2aaSAndroid Build Coastguard Worker 
182*c8dee2aaSAndroid Build Coastguard Worker     struct Stage {
183*c8dee2aaSAndroid Build Coastguard Worker         ProgramOp op;
184*c8dee2aaSAndroid Build Coastguard Worker         void*     ctx;
185*c8dee2aaSAndroid Build Coastguard Worker     };
186*c8dee2aaSAndroid Build Coastguard Worker     void makeStages(skia_private::TArray<Stage>* pipeline,
187*c8dee2aaSAndroid Build Coastguard Worker                     SkArenaAlloc* alloc,
188*c8dee2aaSAndroid Build Coastguard Worker                     SkSpan<const float> uniforms,
189*c8dee2aaSAndroid Build Coastguard Worker                     const SlotData& slots) const;
190*c8dee2aaSAndroid Build Coastguard Worker     StackDepths tempStackMaxDepths() const;
191*c8dee2aaSAndroid Build Coastguard Worker 
192*c8dee2aaSAndroid Build Coastguard Worker     // These methods are used to split up multi-slot copies into multiple ops as needed.
193*c8dee2aaSAndroid Build Coastguard Worker     void appendCopy(skia_private::TArray<Stage>* pipeline,
194*c8dee2aaSAndroid Build Coastguard Worker                     SkArenaAlloc* alloc,
195*c8dee2aaSAndroid Build Coastguard Worker                     std::byte* basePtr,
196*c8dee2aaSAndroid Build Coastguard Worker                     ProgramOp baseStage,
197*c8dee2aaSAndroid Build Coastguard Worker                     SkRPOffset dst, int dstStride,
198*c8dee2aaSAndroid Build Coastguard Worker                     SkRPOffset src, int srcStride,
199*c8dee2aaSAndroid Build Coastguard Worker                     int numSlots) const;
200*c8dee2aaSAndroid Build Coastguard Worker     void appendCopyImmutableUnmasked(skia_private::TArray<Stage>* pipeline,
201*c8dee2aaSAndroid Build Coastguard Worker                                      SkArenaAlloc* alloc,
202*c8dee2aaSAndroid Build Coastguard Worker                                      std::byte* basePtr,
203*c8dee2aaSAndroid Build Coastguard Worker                                      SkRPOffset dst,
204*c8dee2aaSAndroid Build Coastguard Worker                                      SkRPOffset src,
205*c8dee2aaSAndroid Build Coastguard Worker                                      int numSlots) const;
206*c8dee2aaSAndroid Build Coastguard Worker     void appendCopySlotsUnmasked(skia_private::TArray<Stage>* pipeline,
207*c8dee2aaSAndroid Build Coastguard Worker                                  SkArenaAlloc* alloc,
208*c8dee2aaSAndroid Build Coastguard Worker                                  SkRPOffset dst,
209*c8dee2aaSAndroid Build Coastguard Worker                                  SkRPOffset src,
210*c8dee2aaSAndroid Build Coastguard Worker                                  int numSlots) const;
211*c8dee2aaSAndroid Build Coastguard Worker     void appendCopySlotsMasked(skia_private::TArray<Stage>* pipeline,
212*c8dee2aaSAndroid Build Coastguard Worker                                SkArenaAlloc* alloc,
213*c8dee2aaSAndroid Build Coastguard Worker                                SkRPOffset dst,
214*c8dee2aaSAndroid Build Coastguard Worker                                SkRPOffset src,
215*c8dee2aaSAndroid Build Coastguard Worker                                int numSlots) const;
216*c8dee2aaSAndroid Build Coastguard Worker 
217*c8dee2aaSAndroid Build Coastguard Worker     // Appends a single-slot single-input math operation to the pipeline. The op `stage` will
218*c8dee2aaSAndroid Build Coastguard Worker     // appended `numSlots` times, starting at position `dst` and advancing one slot for each
219*c8dee2aaSAndroid Build Coastguard Worker     // subsequent invocation.
220*c8dee2aaSAndroid Build Coastguard Worker     void appendSingleSlotUnaryOp(skia_private::TArray<Stage>* pipeline, ProgramOp stage,
221*c8dee2aaSAndroid Build Coastguard Worker                                  float* dst, int numSlots) const;
222*c8dee2aaSAndroid Build Coastguard Worker 
223*c8dee2aaSAndroid Build Coastguard Worker     // Appends a multi-slot single-input math operation to the pipeline. `baseStage` must refer to
224*c8dee2aaSAndroid Build Coastguard Worker     // a single-slot "apply_op" stage, which must be immediately followed by specializations for
225*c8dee2aaSAndroid Build Coastguard Worker     // 2-4 slots. For instance, {`ceil_float`, `ceil_2_floats`, `ceil_3_floats`, `ceil_4_floats`}
226*c8dee2aaSAndroid Build Coastguard Worker     // must be contiguous ops in the stage list, listed in that order; pass `ceil_float` and we
227*c8dee2aaSAndroid Build Coastguard Worker     // pick the appropriate op based on `numSlots`.
228*c8dee2aaSAndroid Build Coastguard Worker     void appendMultiSlotUnaryOp(skia_private::TArray<Stage>* pipeline, ProgramOp baseStage,
229*c8dee2aaSAndroid Build Coastguard Worker                                 float* dst, int numSlots) const;
230*c8dee2aaSAndroid Build Coastguard Worker 
231*c8dee2aaSAndroid Build Coastguard Worker     // Appends an immediate-mode binary operation to the pipeline. `baseStage` must refer to
232*c8dee2aaSAndroid Build Coastguard Worker     // a single-slot, immediate-mode "apply-imm" stage, which must be immediately preceded by
233*c8dee2aaSAndroid Build Coastguard Worker     // specializations for 2-4 slots if numSlots is greater than 1. For instance, {`add_imm_4_ints`,
234*c8dee2aaSAndroid Build Coastguard Worker     // `add_imm_3_ints`, `add_imm_2_ints`, `add_imm_int`} must be contiguous ops in the stage list,
235*c8dee2aaSAndroid Build Coastguard Worker     // listed in that order; pass `add_imm_int` and we pick the appropriate op based on `numSlots`.
236*c8dee2aaSAndroid Build Coastguard Worker     // Some immediate-mode binary ops are single-slot only in the interest of code size; in this
237*c8dee2aaSAndroid Build Coastguard Worker     // case, the multi-slot ops can be absent, but numSlots must be 1.
238*c8dee2aaSAndroid Build Coastguard Worker     void appendImmediateBinaryOp(skia_private::TArray<Stage>* pipeline, SkArenaAlloc* alloc,
239*c8dee2aaSAndroid Build Coastguard Worker                                  ProgramOp baseStage,
240*c8dee2aaSAndroid Build Coastguard Worker                                  SkRPOffset dst, int32_t value, int numSlots) const;
241*c8dee2aaSAndroid Build Coastguard Worker 
242*c8dee2aaSAndroid Build Coastguard Worker     // Appends a two-input math operation to the pipeline. `src` must be _immediately_ after `dst`
243*c8dee2aaSAndroid Build Coastguard Worker     // in memory. `baseStage` must refer to an unbounded "apply_to_n_slots" stage. A BinaryOpCtx
244*c8dee2aaSAndroid Build Coastguard Worker     // will be used to pass pointers to the destination and source; the delta between the two
245*c8dee2aaSAndroid Build Coastguard Worker     // pointers implicitly gives the number of slots.
246*c8dee2aaSAndroid Build Coastguard Worker     void appendAdjacentNWayBinaryOp(skia_private::TArray<Stage>* pipeline, SkArenaAlloc* alloc,
247*c8dee2aaSAndroid Build Coastguard Worker                                     ProgramOp stage,
248*c8dee2aaSAndroid Build Coastguard Worker                                     SkRPOffset dst, SkRPOffset src, int numSlots) const;
249*c8dee2aaSAndroid Build Coastguard Worker 
250*c8dee2aaSAndroid Build Coastguard Worker     // Appends a multi-slot two-input math operation to the pipeline. `src` must be _immediately_
251*c8dee2aaSAndroid Build Coastguard Worker     // after `dst` in memory. `baseStage` must refer to an unbounded "apply_to_n_slots" stage, which
252*c8dee2aaSAndroid Build Coastguard Worker     // must be immediately followed by specializations for 1-4 slots. For instance, {`add_n_floats`,
253*c8dee2aaSAndroid Build Coastguard Worker     // `add_float`, `add_2_floats`, `add_3_floats`, `add_4_floats`} must be contiguous ops in the
254*c8dee2aaSAndroid Build Coastguard Worker     // stage list, listed in that order; pass `add_n_floats` and we pick the appropriate op based on
255*c8dee2aaSAndroid Build Coastguard Worker     // `numSlots`.
256*c8dee2aaSAndroid Build Coastguard Worker     void appendAdjacentMultiSlotBinaryOp(skia_private::TArray<Stage>* pipeline, SkArenaAlloc* alloc,
257*c8dee2aaSAndroid Build Coastguard Worker                                          ProgramOp baseStage, std::byte* basePtr,
258*c8dee2aaSAndroid Build Coastguard Worker                                          SkRPOffset dst, SkRPOffset src, int numSlots) const;
259*c8dee2aaSAndroid Build Coastguard Worker 
260*c8dee2aaSAndroid Build Coastguard Worker     // Appends a multi-slot math operation having three inputs (dst, src0, src1) and one output
261*c8dee2aaSAndroid Build Coastguard Worker     // (dst) to the pipeline. The three inputs must be _immediately_ adjacent in memory. `baseStage`
262*c8dee2aaSAndroid Build Coastguard Worker     // must refer to an unbounded "apply_to_n_slots" stage, which must be immediately followed by
263*c8dee2aaSAndroid Build Coastguard Worker     // specializations for 1-4 slots.
264*c8dee2aaSAndroid Build Coastguard Worker     void appendAdjacentMultiSlotTernaryOp(skia_private::TArray<Stage>* pipeline,
265*c8dee2aaSAndroid Build Coastguard Worker                                           SkArenaAlloc* alloc, ProgramOp baseStage,
266*c8dee2aaSAndroid Build Coastguard Worker                                           std::byte* basePtr, SkRPOffset dst, SkRPOffset src0,
267*c8dee2aaSAndroid Build Coastguard Worker                                           SkRPOffset src1, int numSlots) const;
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker     // Appends a math operation having three inputs (dst, src0, src1) and one output (dst) to the
270*c8dee2aaSAndroid Build Coastguard Worker     // pipeline. The three inputs must be _immediately_ adjacent in memory. `baseStage` must refer
271*c8dee2aaSAndroid Build Coastguard Worker     // to an unbounded "apply_to_n_slots" stage. A TernaryOpCtx will be used to pass pointers to the
272*c8dee2aaSAndroid Build Coastguard Worker     // destination and sources; the delta between the each pointer implicitly gives the slot count.
273*c8dee2aaSAndroid Build Coastguard Worker     void appendAdjacentNWayTernaryOp(skia_private::TArray<Stage>* pipeline, SkArenaAlloc* alloc,
274*c8dee2aaSAndroid Build Coastguard Worker                                      ProgramOp stage, std::byte* basePtr, SkRPOffset dst,
275*c8dee2aaSAndroid Build Coastguard Worker                                      SkRPOffset src0, SkRPOffset src1, int numSlots) const;
276*c8dee2aaSAndroid Build Coastguard Worker 
277*c8dee2aaSAndroid Build Coastguard Worker     // Appends a stack_rewind op on platforms where it is needed (when SK_HAS_MUSTTAIL is not set).
278*c8dee2aaSAndroid Build Coastguard Worker     void appendStackRewindForNonTailcallers(skia_private::TArray<Stage>* pipeline) const;
279*c8dee2aaSAndroid Build Coastguard Worker 
280*c8dee2aaSAndroid Build Coastguard Worker     // Appends a stack_rewind op unilaterally.
281*c8dee2aaSAndroid Build Coastguard Worker     void appendStackRewind(skia_private::TArray<Stage>* pipeline) const;
282*c8dee2aaSAndroid Build Coastguard Worker 
283*c8dee2aaSAndroid Build Coastguard Worker     class Dumper;
284*c8dee2aaSAndroid Build Coastguard Worker     friend class Dumper;
285*c8dee2aaSAndroid Build Coastguard Worker 
286*c8dee2aaSAndroid Build Coastguard Worker     skia_private::TArray<Instruction> fInstructions;
287*c8dee2aaSAndroid Build Coastguard Worker     int fNumValueSlots = 0;
288*c8dee2aaSAndroid Build Coastguard Worker     int fNumUniformSlots = 0;
289*c8dee2aaSAndroid Build Coastguard Worker     int fNumImmutableSlots = 0;
290*c8dee2aaSAndroid Build Coastguard Worker     int fNumTempStackSlots = 0;
291*c8dee2aaSAndroid Build Coastguard Worker     int fNumLabels = 0;
292*c8dee2aaSAndroid Build Coastguard Worker     StackDepths fTempStackMaxDepths;
293*c8dee2aaSAndroid Build Coastguard Worker     DebugTracePriv* fDebugTrace = nullptr;
294*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<SkSL::TraceHook> fTraceHook;
295*c8dee2aaSAndroid Build Coastguard Worker };
296*c8dee2aaSAndroid Build Coastguard Worker 
297*c8dee2aaSAndroid Build Coastguard Worker class Builder {
298*c8dee2aaSAndroid Build Coastguard Worker public:
299*c8dee2aaSAndroid Build Coastguard Worker     /** Finalizes and returns a completed program. */
300*c8dee2aaSAndroid Build Coastguard Worker     std::unique_ptr<Program> finish(int numValueSlots,
301*c8dee2aaSAndroid Build Coastguard Worker                                     int numUniformSlots,
302*c8dee2aaSAndroid Build Coastguard Worker                                     int numImmutableSlots,
303*c8dee2aaSAndroid Build Coastguard Worker                                     DebugTracePriv* debugTrace = nullptr);
304*c8dee2aaSAndroid Build Coastguard Worker     /**
305*c8dee2aaSAndroid Build Coastguard Worker      * Peels off a label ID for use in the program. Set the label's position in the program with
306*c8dee2aaSAndroid Build Coastguard Worker      * the `label` instruction. Actually branch to the target with an instruction like
307*c8dee2aaSAndroid Build Coastguard Worker      * `branch_if_any_lanes_active` or `jump`.
308*c8dee2aaSAndroid Build Coastguard Worker      */
nextLabelID()309*c8dee2aaSAndroid Build Coastguard Worker     int nextLabelID() {
310*c8dee2aaSAndroid Build Coastguard Worker         return fNumLabels++;
311*c8dee2aaSAndroid Build Coastguard Worker     }
312*c8dee2aaSAndroid Build Coastguard Worker 
313*c8dee2aaSAndroid Build Coastguard Worker     /**
314*c8dee2aaSAndroid Build Coastguard Worker      * The builder keeps track of the state of execution masks; when we know that the execution
315*c8dee2aaSAndroid Build Coastguard Worker      * mask is unaltered, we can generate simpler code. Code which alters the execution mask is
316*c8dee2aaSAndroid Build Coastguard Worker      * required to enable this flag.
317*c8dee2aaSAndroid Build Coastguard Worker      */
enableExecutionMaskWrites()318*c8dee2aaSAndroid Build Coastguard Worker     void enableExecutionMaskWrites() {
319*c8dee2aaSAndroid Build Coastguard Worker         ++fExecutionMaskWritesEnabled;
320*c8dee2aaSAndroid Build Coastguard Worker     }
321*c8dee2aaSAndroid Build Coastguard Worker 
disableExecutionMaskWrites()322*c8dee2aaSAndroid Build Coastguard Worker     void disableExecutionMaskWrites() {
323*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
324*c8dee2aaSAndroid Build Coastguard Worker         --fExecutionMaskWritesEnabled;
325*c8dee2aaSAndroid Build Coastguard Worker     }
326*c8dee2aaSAndroid Build Coastguard Worker 
executionMaskWritesAreEnabled()327*c8dee2aaSAndroid Build Coastguard Worker     bool executionMaskWritesAreEnabled() {
328*c8dee2aaSAndroid Build Coastguard Worker         return fExecutionMaskWritesEnabled > 0;
329*c8dee2aaSAndroid Build Coastguard Worker     }
330*c8dee2aaSAndroid Build Coastguard Worker 
331*c8dee2aaSAndroid Build Coastguard Worker     /** Assemble a program from the Raster Pipeline instructions below. */
init_lane_masks()332*c8dee2aaSAndroid Build Coastguard Worker     void init_lane_masks() {
333*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::init_lane_masks, {});
334*c8dee2aaSAndroid Build Coastguard Worker     }
335*c8dee2aaSAndroid Build Coastguard Worker 
store_src_rg(SlotRange slots)336*c8dee2aaSAndroid Build Coastguard Worker     void store_src_rg(SlotRange slots) {
337*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(slots.count == 2);
338*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::store_src_rg, {slots.index});
339*c8dee2aaSAndroid Build Coastguard Worker     }
340*c8dee2aaSAndroid Build Coastguard Worker 
store_src(SlotRange slots)341*c8dee2aaSAndroid Build Coastguard Worker     void store_src(SlotRange slots) {
342*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(slots.count == 4);
343*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::store_src, {slots.index});
344*c8dee2aaSAndroid Build Coastguard Worker     }
345*c8dee2aaSAndroid Build Coastguard Worker 
store_dst(SlotRange slots)346*c8dee2aaSAndroid Build Coastguard Worker     void store_dst(SlotRange slots) {
347*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(slots.count == 4);
348*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::store_dst, {slots.index});
349*c8dee2aaSAndroid Build Coastguard Worker     }
350*c8dee2aaSAndroid Build Coastguard Worker 
store_device_xy01(SlotRange slots)351*c8dee2aaSAndroid Build Coastguard Worker     void store_device_xy01(SlotRange slots) {
352*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(slots.count == 4);
353*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::store_device_xy01, {slots.index});
354*c8dee2aaSAndroid Build Coastguard Worker     }
355*c8dee2aaSAndroid Build Coastguard Worker 
load_src(SlotRange slots)356*c8dee2aaSAndroid Build Coastguard Worker     void load_src(SlotRange slots) {
357*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(slots.count == 4);
358*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::load_src, {slots.index});
359*c8dee2aaSAndroid Build Coastguard Worker     }
360*c8dee2aaSAndroid Build Coastguard Worker 
load_dst(SlotRange slots)361*c8dee2aaSAndroid Build Coastguard Worker     void load_dst(SlotRange slots) {
362*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(slots.count == 4);
363*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::load_dst, {slots.index});
364*c8dee2aaSAndroid Build Coastguard Worker     }
365*c8dee2aaSAndroid Build Coastguard Worker 
set_current_stack(int stackID)366*c8dee2aaSAndroid Build Coastguard Worker     void set_current_stack(int stackID) {
367*c8dee2aaSAndroid Build Coastguard Worker         fCurrentStackID = stackID;
368*c8dee2aaSAndroid Build Coastguard Worker     }
369*c8dee2aaSAndroid Build Coastguard Worker 
370*c8dee2aaSAndroid Build Coastguard Worker     // Inserts a label into the instruction stream.
371*c8dee2aaSAndroid Build Coastguard Worker     void label(int labelID);
372*c8dee2aaSAndroid Build Coastguard Worker 
373*c8dee2aaSAndroid Build Coastguard Worker     // Unconditionally branches to a label.
374*c8dee2aaSAndroid Build Coastguard Worker     void jump(int labelID);
375*c8dee2aaSAndroid Build Coastguard Worker 
376*c8dee2aaSAndroid Build Coastguard Worker     // Branches to a label if the execution mask is active in every lane.
377*c8dee2aaSAndroid Build Coastguard Worker     void branch_if_all_lanes_active(int labelID);
378*c8dee2aaSAndroid Build Coastguard Worker 
379*c8dee2aaSAndroid Build Coastguard Worker     // Branches to a label if the execution mask is active in any lane.
380*c8dee2aaSAndroid Build Coastguard Worker     void branch_if_any_lanes_active(int labelID);
381*c8dee2aaSAndroid Build Coastguard Worker 
382*c8dee2aaSAndroid Build Coastguard Worker     // Branches to a label if the execution mask is inactive across all lanes.
383*c8dee2aaSAndroid Build Coastguard Worker     void branch_if_no_lanes_active(int labelID);
384*c8dee2aaSAndroid Build Coastguard Worker 
385*c8dee2aaSAndroid Build Coastguard Worker     // Branches to a label if the top value on the stack is _not_ equal to `value` in any lane.
386*c8dee2aaSAndroid Build Coastguard Worker     void branch_if_no_active_lanes_on_stack_top_equal(int value, int labelID);
387*c8dee2aaSAndroid Build Coastguard Worker 
388*c8dee2aaSAndroid Build Coastguard Worker     // We use the same SkRasterPipeline op regardless of the literal type, and bitcast the value.
389*c8dee2aaSAndroid Build Coastguard Worker     void push_constant_i(int32_t val, int count = 1);
390*c8dee2aaSAndroid Build Coastguard Worker 
push_zeros(int count)391*c8dee2aaSAndroid Build Coastguard Worker     void push_zeros(int count) {
392*c8dee2aaSAndroid Build Coastguard Worker         this->push_constant_i(/*val=*/0, count);
393*c8dee2aaSAndroid Build Coastguard Worker     }
394*c8dee2aaSAndroid Build Coastguard Worker 
push_constant_f(float val)395*c8dee2aaSAndroid Build Coastguard Worker     void push_constant_f(float val) {
396*c8dee2aaSAndroid Build Coastguard Worker         this->push_constant_i(sk_bit_cast<int32_t>(val), /*count=*/1);
397*c8dee2aaSAndroid Build Coastguard Worker     }
398*c8dee2aaSAndroid Build Coastguard Worker 
399*c8dee2aaSAndroid Build Coastguard Worker     void push_constant_u(uint32_t val, int count = 1) {
400*c8dee2aaSAndroid Build Coastguard Worker         this->push_constant_i(sk_bit_cast<int32_t>(val), count);
401*c8dee2aaSAndroid Build Coastguard Worker     }
402*c8dee2aaSAndroid Build Coastguard Worker 
403*c8dee2aaSAndroid Build Coastguard Worker     // Translates into copy_uniforms (from uniforms into temp stack) in Raster Pipeline.
404*c8dee2aaSAndroid Build Coastguard Worker     void push_uniform(SlotRange src);
405*c8dee2aaSAndroid Build Coastguard Worker 
406*c8dee2aaSAndroid Build Coastguard Worker     // Initializes the Raster Pipeline slot with a constant value when the program is first created.
407*c8dee2aaSAndroid Build Coastguard Worker     // Does not add any instructions to the program.
store_immutable_value_i(Slot slot,int32_t val)408*c8dee2aaSAndroid Build Coastguard Worker     void store_immutable_value_i(Slot slot, int32_t val) {
409*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::store_immutable_value, {slot}, val);
410*c8dee2aaSAndroid Build Coastguard Worker     }
411*c8dee2aaSAndroid Build Coastguard Worker 
412*c8dee2aaSAndroid Build Coastguard Worker     // Translates into copy_uniforms (from uniforms into value-slots) in Raster Pipeline.
413*c8dee2aaSAndroid Build Coastguard Worker     void copy_uniform_to_slots_unmasked(SlotRange dst, SlotRange src);
414*c8dee2aaSAndroid Build Coastguard Worker 
415*c8dee2aaSAndroid Build Coastguard Worker     // Translates into copy_from_indirect_uniform_unmasked (from values into temp stack) in Raster
416*c8dee2aaSAndroid Build Coastguard Worker     // Pipeline. `fixedRange` denotes a fixed set of slots; this range is pushed forward by the
417*c8dee2aaSAndroid Build Coastguard Worker     // value at the top of stack `dynamicStack`. Pass the range of the uniform being indexed as
418*c8dee2aaSAndroid Build Coastguard Worker     // `limitRange`; this is used as a hard cap, to avoid indexing outside of bounds.
419*c8dee2aaSAndroid Build Coastguard Worker     void push_uniform_indirect(SlotRange fixedRange, int dynamicStack, SlotRange limitRange);
420*c8dee2aaSAndroid Build Coastguard Worker 
421*c8dee2aaSAndroid Build Coastguard Worker 
422*c8dee2aaSAndroid Build Coastguard Worker     // Translates into copy_slots_unmasked (from values into temp stack) in Raster Pipeline.
push_slots(SlotRange src)423*c8dee2aaSAndroid Build Coastguard Worker     void push_slots(SlotRange src) {
424*c8dee2aaSAndroid Build Coastguard Worker         this->push_slots_or_immutable(src, BuilderOp::push_slots);
425*c8dee2aaSAndroid Build Coastguard Worker     }
426*c8dee2aaSAndroid Build Coastguard Worker 
427*c8dee2aaSAndroid Build Coastguard Worker     // Translates into copy_immutable_unmasked (from immutables into temp stack) in Raster Pipeline.
push_immutable(SlotRange src)428*c8dee2aaSAndroid Build Coastguard Worker     void push_immutable(SlotRange src) {
429*c8dee2aaSAndroid Build Coastguard Worker         this->push_slots_or_immutable(src, BuilderOp::push_immutable);
430*c8dee2aaSAndroid Build Coastguard Worker     }
431*c8dee2aaSAndroid Build Coastguard Worker 
432*c8dee2aaSAndroid Build Coastguard Worker     void push_slots_or_immutable(SlotRange src, BuilderOp op);
433*c8dee2aaSAndroid Build Coastguard Worker 
434*c8dee2aaSAndroid Build Coastguard Worker     // Translates into copy_from_indirect_unmasked (from values into temp stack) in Raster Pipeline.
435*c8dee2aaSAndroid Build Coastguard Worker     // `fixedRange` denotes a fixed set of slots; this range is pushed forward by the value at the
436*c8dee2aaSAndroid Build Coastguard Worker     // top of stack `dynamicStack`. Pass the slot range of the variable being indexed as
437*c8dee2aaSAndroid Build Coastguard Worker     // `limitRange`; this is used as a hard cap, to avoid indexing outside of bounds.
push_slots_indirect(SlotRange fixedRange,int dynamicStack,SlotRange limitRange)438*c8dee2aaSAndroid Build Coastguard Worker     void push_slots_indirect(SlotRange fixedRange, int dynamicStack, SlotRange limitRange) {
439*c8dee2aaSAndroid Build Coastguard Worker         this->push_slots_or_immutable_indirect(fixedRange, dynamicStack, limitRange,
440*c8dee2aaSAndroid Build Coastguard Worker                                                BuilderOp::push_slots_indirect);
441*c8dee2aaSAndroid Build Coastguard Worker     }
442*c8dee2aaSAndroid Build Coastguard Worker 
push_immutable_indirect(SlotRange fixedRange,int dynamicStack,SlotRange limitRange)443*c8dee2aaSAndroid Build Coastguard Worker     void push_immutable_indirect(SlotRange fixedRange, int dynamicStack, SlotRange limitRange) {
444*c8dee2aaSAndroid Build Coastguard Worker         this->push_slots_or_immutable_indirect(fixedRange, dynamicStack, limitRange,
445*c8dee2aaSAndroid Build Coastguard Worker                                                BuilderOp::push_immutable_indirect);
446*c8dee2aaSAndroid Build Coastguard Worker     }
447*c8dee2aaSAndroid Build Coastguard Worker 
448*c8dee2aaSAndroid Build Coastguard Worker     void push_slots_or_immutable_indirect(SlotRange fixedRange, int dynamicStack,
449*c8dee2aaSAndroid Build Coastguard Worker                                           SlotRange limitRange, BuilderOp op);
450*c8dee2aaSAndroid Build Coastguard Worker 
451*c8dee2aaSAndroid Build Coastguard Worker     // Translates into copy_slots_masked (from temp stack to values) in Raster Pipeline.
452*c8dee2aaSAndroid Build Coastguard Worker     // Does not discard any values on the temp stack.
copy_stack_to_slots(SlotRange dst)453*c8dee2aaSAndroid Build Coastguard Worker     void copy_stack_to_slots(SlotRange dst) {
454*c8dee2aaSAndroid Build Coastguard Worker         this->copy_stack_to_slots(dst, /*offsetFromStackTop=*/dst.count);
455*c8dee2aaSAndroid Build Coastguard Worker     }
456*c8dee2aaSAndroid Build Coastguard Worker 
457*c8dee2aaSAndroid Build Coastguard Worker     void copy_stack_to_slots(SlotRange dst, int offsetFromStackTop);
458*c8dee2aaSAndroid Build Coastguard Worker 
459*c8dee2aaSAndroid Build Coastguard Worker     // Translates into swizzle_copy_slots_masked (from temp stack to values) in Raster Pipeline.
460*c8dee2aaSAndroid Build Coastguard Worker     // Does not discard any values on the temp stack.
461*c8dee2aaSAndroid Build Coastguard Worker     void swizzle_copy_stack_to_slots(SlotRange dst,
462*c8dee2aaSAndroid Build Coastguard Worker                                      SkSpan<const int8_t> components,
463*c8dee2aaSAndroid Build Coastguard Worker                                      int offsetFromStackTop);
464*c8dee2aaSAndroid Build Coastguard Worker 
465*c8dee2aaSAndroid Build Coastguard Worker     // Translates into swizzle_copy_to_indirect_masked (from temp stack to values) in Raster
466*c8dee2aaSAndroid Build Coastguard Worker     // Pipeline. Does not discard any values on the temp stack.
467*c8dee2aaSAndroid Build Coastguard Worker     void swizzle_copy_stack_to_slots_indirect(SlotRange fixedRange,
468*c8dee2aaSAndroid Build Coastguard Worker                                               int dynamicStackID,
469*c8dee2aaSAndroid Build Coastguard Worker                                               SlotRange limitRange,
470*c8dee2aaSAndroid Build Coastguard Worker                                               SkSpan<const int8_t> components,
471*c8dee2aaSAndroid Build Coastguard Worker                                               int offsetFromStackTop);
472*c8dee2aaSAndroid Build Coastguard Worker 
473*c8dee2aaSAndroid Build Coastguard Worker     // Translates into copy_slots_unmasked (from temp stack to values) in Raster Pipeline.
474*c8dee2aaSAndroid Build Coastguard Worker     // Does not discard any values on the temp stack.
copy_stack_to_slots_unmasked(SlotRange dst)475*c8dee2aaSAndroid Build Coastguard Worker     void copy_stack_to_slots_unmasked(SlotRange dst) {
476*c8dee2aaSAndroid Build Coastguard Worker         this->copy_stack_to_slots_unmasked(dst, /*offsetFromStackTop=*/dst.count);
477*c8dee2aaSAndroid Build Coastguard Worker     }
478*c8dee2aaSAndroid Build Coastguard Worker 
479*c8dee2aaSAndroid Build Coastguard Worker     void copy_stack_to_slots_unmasked(SlotRange dst, int offsetFromStackTop);
480*c8dee2aaSAndroid Build Coastguard Worker 
481*c8dee2aaSAndroid Build Coastguard Worker     // Translates into copy_to_indirect_masked (from temp stack into values) in Raster Pipeline.
482*c8dee2aaSAndroid Build Coastguard Worker     // `fixedRange` denotes a fixed set of slots; this range is pushed forward by the value at the
483*c8dee2aaSAndroid Build Coastguard Worker     // top of stack `dynamicStack`. Pass the slot range of the variable being indexed as
484*c8dee2aaSAndroid Build Coastguard Worker     // `limitRange`; this is used as a hard cap, to avoid indexing outside of bounds.
485*c8dee2aaSAndroid Build Coastguard Worker     void copy_stack_to_slots_indirect(SlotRange fixedRange,
486*c8dee2aaSAndroid Build Coastguard Worker                                       int dynamicStackID,
487*c8dee2aaSAndroid Build Coastguard Worker                                       SlotRange limitRange);
488*c8dee2aaSAndroid Build Coastguard Worker 
489*c8dee2aaSAndroid Build Coastguard Worker     // Copies from temp stack to slots, including an indirect offset, then shrinks the temp stack.
pop_slots_indirect(SlotRange fixedRange,int dynamicStackID,SlotRange limitRange)490*c8dee2aaSAndroid Build Coastguard Worker     void pop_slots_indirect(SlotRange fixedRange, int dynamicStackID, SlotRange limitRange) {
491*c8dee2aaSAndroid Build Coastguard Worker         this->copy_stack_to_slots_indirect(fixedRange, dynamicStackID, limitRange);
492*c8dee2aaSAndroid Build Coastguard Worker         this->discard_stack(fixedRange.count);
493*c8dee2aaSAndroid Build Coastguard Worker     }
494*c8dee2aaSAndroid Build Coastguard Worker 
495*c8dee2aaSAndroid Build Coastguard Worker     // Performs a unary op (like `bitwise_not`), given a slot count of `slots`. The stack top is
496*c8dee2aaSAndroid Build Coastguard Worker     // replaced with the result.
497*c8dee2aaSAndroid Build Coastguard Worker     void unary_op(BuilderOp op, int32_t slots);
498*c8dee2aaSAndroid Build Coastguard Worker 
499*c8dee2aaSAndroid Build Coastguard Worker     // Performs a binary op (like `add_n_floats` or `cmpeq_n_ints`), given a slot count of
500*c8dee2aaSAndroid Build Coastguard Worker     // `slots`. Two n-slot input values are consumed, and the result is pushed onto the stack.
501*c8dee2aaSAndroid Build Coastguard Worker     void binary_op(BuilderOp op, int32_t slots);
502*c8dee2aaSAndroid Build Coastguard Worker 
503*c8dee2aaSAndroid Build Coastguard Worker     // Performs a ternary op (like `mix` or `smoothstep`), given a slot count of
504*c8dee2aaSAndroid Build Coastguard Worker     // `slots`. Three n-slot input values are consumed, and the result is pushed onto the stack.
505*c8dee2aaSAndroid Build Coastguard Worker     void ternary_op(BuilderOp op, int32_t slots);
506*c8dee2aaSAndroid Build Coastguard Worker 
507*c8dee2aaSAndroid Build Coastguard Worker     // Computes a dot product on the stack. The slots consumed (`slots`) must be between 1 and 4.
508*c8dee2aaSAndroid Build Coastguard Worker     // Two n-slot input vectors are consumed, and a scalar result is pushed onto the stack.
509*c8dee2aaSAndroid Build Coastguard Worker     void dot_floats(int32_t slots);
510*c8dee2aaSAndroid Build Coastguard Worker 
511*c8dee2aaSAndroid Build Coastguard Worker     // Computes refract(N, I, eta) on the stack. N and I are assumed to be 4-slot vectors, and can
512*c8dee2aaSAndroid Build Coastguard Worker     // be padded with zeros for smaller inputs. Eta is a scalar. The result is a 4-slot vector.
513*c8dee2aaSAndroid Build Coastguard Worker     void refract_floats();
514*c8dee2aaSAndroid Build Coastguard Worker 
515*c8dee2aaSAndroid Build Coastguard Worker     // Computes inverse(matN) on the stack. Pass 2, 3 or 4 for n to specify matrix size.
516*c8dee2aaSAndroid Build Coastguard Worker     void inverse_matrix(int32_t n);
517*c8dee2aaSAndroid Build Coastguard Worker 
518*c8dee2aaSAndroid Build Coastguard Worker     // Shrinks the temp stack, discarding values on top.
519*c8dee2aaSAndroid Build Coastguard Worker     void discard_stack(int32_t count, int stackID);
520*c8dee2aaSAndroid Build Coastguard Worker 
discard_stack(int32_t count)521*c8dee2aaSAndroid Build Coastguard Worker     void discard_stack(int32_t count) {
522*c8dee2aaSAndroid Build Coastguard Worker         this->discard_stack(count, fCurrentStackID);
523*c8dee2aaSAndroid Build Coastguard Worker     }
524*c8dee2aaSAndroid Build Coastguard Worker 
525*c8dee2aaSAndroid Build Coastguard Worker     // Grows the temp stack, leaving any preexisting values in place.
526*c8dee2aaSAndroid Build Coastguard Worker     void pad_stack(int32_t count);
527*c8dee2aaSAndroid Build Coastguard Worker 
528*c8dee2aaSAndroid Build Coastguard Worker     // Copies vales from the temp stack into slots, and then shrinks the temp stack.
529*c8dee2aaSAndroid Build Coastguard Worker     void pop_slots(SlotRange dst);
530*c8dee2aaSAndroid Build Coastguard Worker 
531*c8dee2aaSAndroid Build Coastguard Worker     // Creates many clones of the top single-slot item on the temp stack.
532*c8dee2aaSAndroid Build Coastguard Worker     void push_duplicates(int count);
533*c8dee2aaSAndroid Build Coastguard Worker 
534*c8dee2aaSAndroid Build Coastguard Worker     // Creates a single clone of an item on the current temp stack. The cloned item can consist of
535*c8dee2aaSAndroid Build Coastguard Worker     // any number of slots, and can be copied from an earlier position on the stack.
536*c8dee2aaSAndroid Build Coastguard Worker     void push_clone(int numSlots, int offsetFromStackTop = 0);
537*c8dee2aaSAndroid Build Coastguard Worker 
538*c8dee2aaSAndroid Build Coastguard Worker     // Clones a range of slots from another stack onto this stack.
539*c8dee2aaSAndroid Build Coastguard Worker     void push_clone_from_stack(SlotRange range, int otherStackID, int offsetFromStackTop);
540*c8dee2aaSAndroid Build Coastguard Worker 
541*c8dee2aaSAndroid Build Coastguard Worker     // Translates into copy_from_indirect_unmasked (from one temp stack to another) in Raster
542*c8dee2aaSAndroid Build Coastguard Worker     // Pipeline. `fixedOffset` denotes a range of slots within the top `offsetFromStackTop` slots of
543*c8dee2aaSAndroid Build Coastguard Worker     // `otherStackID`. This range is pushed forward by the value at the top of `dynamicStackID`.
544*c8dee2aaSAndroid Build Coastguard Worker     void push_clone_indirect_from_stack(SlotRange fixedOffset,
545*c8dee2aaSAndroid Build Coastguard Worker                                         int dynamicStackID,
546*c8dee2aaSAndroid Build Coastguard Worker                                         int otherStackID,
547*c8dee2aaSAndroid Build Coastguard Worker                                         int offsetFromStackTop);
548*c8dee2aaSAndroid Build Coastguard Worker 
549*c8dee2aaSAndroid Build Coastguard Worker     // Compares the stack top with the passed-in value; if it matches, enables the loop mask.
case_op(int value)550*c8dee2aaSAndroid Build Coastguard Worker     void case_op(int value) {
551*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::case_op, {}, value);
552*c8dee2aaSAndroid Build Coastguard Worker     }
553*c8dee2aaSAndroid Build Coastguard Worker 
554*c8dee2aaSAndroid Build Coastguard Worker     // Performs a `continue` in a loop.
continue_op(int continueMaskStackID)555*c8dee2aaSAndroid Build Coastguard Worker     void continue_op(int continueMaskStackID) {
556*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::continue_op, {}, continueMaskStackID);
557*c8dee2aaSAndroid Build Coastguard Worker     }
558*c8dee2aaSAndroid Build Coastguard Worker 
select(int slots)559*c8dee2aaSAndroid Build Coastguard Worker     void select(int slots) {
560*c8dee2aaSAndroid Build Coastguard Worker         // Overlays the top two entries on the stack, making one hybrid entry. The execution mask
561*c8dee2aaSAndroid Build Coastguard Worker         // is used to select which lanes are preserved.
562*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(slots > 0);
563*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::select, {}, slots);
564*c8dee2aaSAndroid Build Coastguard Worker     }
565*c8dee2aaSAndroid Build Coastguard Worker 
566*c8dee2aaSAndroid Build Coastguard Worker     // The opposite of push_slots; copies values from the temp stack into value slots, then
567*c8dee2aaSAndroid Build Coastguard Worker     // shrinks the temp stack.
568*c8dee2aaSAndroid Build Coastguard Worker     void pop_slots_unmasked(SlotRange dst);
569*c8dee2aaSAndroid Build Coastguard Worker 
copy_slots_masked(SlotRange dst,SlotRange src)570*c8dee2aaSAndroid Build Coastguard Worker     void copy_slots_masked(SlotRange dst, SlotRange src) {
571*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(dst.count == src.count);
572*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::copy_slot_masked, {dst.index, src.index}, dst.count);
573*c8dee2aaSAndroid Build Coastguard Worker     }
574*c8dee2aaSAndroid Build Coastguard Worker 
575*c8dee2aaSAndroid Build Coastguard Worker     void copy_slots_unmasked(SlotRange dst, SlotRange src);
576*c8dee2aaSAndroid Build Coastguard Worker 
577*c8dee2aaSAndroid Build Coastguard Worker     void copy_immutable_unmasked(SlotRange dst, SlotRange src);
578*c8dee2aaSAndroid Build Coastguard Worker 
579*c8dee2aaSAndroid Build Coastguard Worker     // Directly writes a constant value into a slot.
580*c8dee2aaSAndroid Build Coastguard Worker     void copy_constant(Slot slot, int constantValue);
581*c8dee2aaSAndroid Build Coastguard Worker 
582*c8dee2aaSAndroid Build Coastguard Worker     // Stores zeros across the entire slot range.
583*c8dee2aaSAndroid Build Coastguard Worker     void zero_slots_unmasked(SlotRange dst);
584*c8dee2aaSAndroid Build Coastguard Worker 
585*c8dee2aaSAndroid Build Coastguard Worker     // Consumes `consumedSlots` elements on the stack, then generates `components.size()` elements.
586*c8dee2aaSAndroid Build Coastguard Worker     void swizzle(int consumedSlots, SkSpan<const int8_t> components);
587*c8dee2aaSAndroid Build Coastguard Worker 
588*c8dee2aaSAndroid Build Coastguard Worker     // Transposes a matrix of size CxR on the stack (into a matrix of size RxC).
589*c8dee2aaSAndroid Build Coastguard Worker     void transpose(int columns, int rows);
590*c8dee2aaSAndroid Build Coastguard Worker 
591*c8dee2aaSAndroid Build Coastguard Worker     // Generates a CxR diagonal matrix from the top two scalars on the stack. The second scalar is
592*c8dee2aaSAndroid Build Coastguard Worker     // used as the diagonal value; the first scalar (usually zero) fills in the rest of the slots.
593*c8dee2aaSAndroid Build Coastguard Worker     void diagonal_matrix(int columns, int rows);
594*c8dee2aaSAndroid Build Coastguard Worker 
595*c8dee2aaSAndroid Build Coastguard Worker     // Resizes a CxR matrix at the top of the stack to C'xR'.
596*c8dee2aaSAndroid Build Coastguard Worker     void matrix_resize(int origColumns, int origRows, int newColumns, int newRows);
597*c8dee2aaSAndroid Build Coastguard Worker 
598*c8dee2aaSAndroid Build Coastguard Worker     // Multiplies a CxR matrix/vector against an adjacent CxR matrix/vector on the stack.
599*c8dee2aaSAndroid Build Coastguard Worker     void matrix_multiply(int leftColumns, int leftRows, int rightColumns, int rightRows);
600*c8dee2aaSAndroid Build Coastguard Worker 
601*c8dee2aaSAndroid Build Coastguard Worker     void push_condition_mask();
602*c8dee2aaSAndroid Build Coastguard Worker 
pop_condition_mask()603*c8dee2aaSAndroid Build Coastguard Worker     void pop_condition_mask() {
604*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
605*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::pop_condition_mask, {});
606*c8dee2aaSAndroid Build Coastguard Worker     }
607*c8dee2aaSAndroid Build Coastguard Worker 
608*c8dee2aaSAndroid Build Coastguard Worker     void merge_condition_mask();
609*c8dee2aaSAndroid Build Coastguard Worker 
merge_inv_condition_mask()610*c8dee2aaSAndroid Build Coastguard Worker     void merge_inv_condition_mask() {
611*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
612*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::merge_inv_condition_mask, {});
613*c8dee2aaSAndroid Build Coastguard Worker     }
614*c8dee2aaSAndroid Build Coastguard Worker 
push_loop_mask()615*c8dee2aaSAndroid Build Coastguard Worker     void push_loop_mask() {
616*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
617*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::push_loop_mask, {});
618*c8dee2aaSAndroid Build Coastguard Worker     }
619*c8dee2aaSAndroid Build Coastguard Worker 
pop_loop_mask()620*c8dee2aaSAndroid Build Coastguard Worker     void pop_loop_mask() {
621*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
622*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::pop_loop_mask, {});
623*c8dee2aaSAndroid Build Coastguard Worker     }
624*c8dee2aaSAndroid Build Coastguard Worker 
625*c8dee2aaSAndroid Build Coastguard Worker     // Exchanges src.rgba with the four values at the top of the stack.
626*c8dee2aaSAndroid Build Coastguard Worker     void exchange_src();
627*c8dee2aaSAndroid Build Coastguard Worker 
push_src_rgba()628*c8dee2aaSAndroid Build Coastguard Worker     void push_src_rgba() {
629*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::push_src_rgba, {});
630*c8dee2aaSAndroid Build Coastguard Worker     }
631*c8dee2aaSAndroid Build Coastguard Worker 
push_dst_rgba()632*c8dee2aaSAndroid Build Coastguard Worker     void push_dst_rgba() {
633*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::push_dst_rgba, {});
634*c8dee2aaSAndroid Build Coastguard Worker     }
635*c8dee2aaSAndroid Build Coastguard Worker 
push_device_xy01()636*c8dee2aaSAndroid Build Coastguard Worker     void push_device_xy01() {
637*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::push_device_xy01, {});
638*c8dee2aaSAndroid Build Coastguard Worker     }
639*c8dee2aaSAndroid Build Coastguard Worker 
640*c8dee2aaSAndroid Build Coastguard Worker     void pop_src_rgba();
641*c8dee2aaSAndroid Build Coastguard Worker 
pop_dst_rgba()642*c8dee2aaSAndroid Build Coastguard Worker     void pop_dst_rgba() {
643*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::pop_dst_rgba, {});
644*c8dee2aaSAndroid Build Coastguard Worker     }
645*c8dee2aaSAndroid Build Coastguard Worker 
mask_off_loop_mask()646*c8dee2aaSAndroid Build Coastguard Worker     void mask_off_loop_mask() {
647*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
648*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::mask_off_loop_mask, {});
649*c8dee2aaSAndroid Build Coastguard Worker     }
650*c8dee2aaSAndroid Build Coastguard Worker 
reenable_loop_mask(SlotRange src)651*c8dee2aaSAndroid Build Coastguard Worker     void reenable_loop_mask(SlotRange src) {
652*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
653*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(src.count == 1);
654*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::reenable_loop_mask, {src.index});
655*c8dee2aaSAndroid Build Coastguard Worker     }
656*c8dee2aaSAndroid Build Coastguard Worker 
pop_and_reenable_loop_mask()657*c8dee2aaSAndroid Build Coastguard Worker     void pop_and_reenable_loop_mask() {
658*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
659*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::pop_and_reenable_loop_mask, {});
660*c8dee2aaSAndroid Build Coastguard Worker     }
661*c8dee2aaSAndroid Build Coastguard Worker 
merge_loop_mask()662*c8dee2aaSAndroid Build Coastguard Worker     void merge_loop_mask() {
663*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
664*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::merge_loop_mask, {});
665*c8dee2aaSAndroid Build Coastguard Worker     }
666*c8dee2aaSAndroid Build Coastguard Worker 
push_return_mask()667*c8dee2aaSAndroid Build Coastguard Worker     void push_return_mask() {
668*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
669*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::push_return_mask, {});
670*c8dee2aaSAndroid Build Coastguard Worker     }
671*c8dee2aaSAndroid Build Coastguard Worker 
672*c8dee2aaSAndroid Build Coastguard Worker     void pop_return_mask();
673*c8dee2aaSAndroid Build Coastguard Worker 
mask_off_return_mask()674*c8dee2aaSAndroid Build Coastguard Worker     void mask_off_return_mask() {
675*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(this->executionMaskWritesAreEnabled());
676*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::mask_off_return_mask, {});
677*c8dee2aaSAndroid Build Coastguard Worker     }
678*c8dee2aaSAndroid Build Coastguard Worker 
679*c8dee2aaSAndroid Build Coastguard Worker     void invoke_shader(int childIdx);
680*c8dee2aaSAndroid Build Coastguard Worker     void invoke_color_filter(int childIdx);
681*c8dee2aaSAndroid Build Coastguard Worker     void invoke_blender(int childIdx);
682*c8dee2aaSAndroid Build Coastguard Worker     void invoke_to_linear_srgb();
683*c8dee2aaSAndroid Build Coastguard Worker     void invoke_from_linear_srgb();
684*c8dee2aaSAndroid Build Coastguard Worker 
685*c8dee2aaSAndroid Build Coastguard Worker     // Writes the current line number to the debug trace.
trace_line(int traceMaskStackID,int line)686*c8dee2aaSAndroid Build Coastguard Worker     void trace_line(int traceMaskStackID, int line) {
687*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::trace_line, {}, traceMaskStackID, line);
688*c8dee2aaSAndroid Build Coastguard Worker     }
689*c8dee2aaSAndroid Build Coastguard Worker 
690*c8dee2aaSAndroid Build Coastguard Worker     // Writes a variable update to the debug trace.
trace_var(int traceMaskStackID,SlotRange r)691*c8dee2aaSAndroid Build Coastguard Worker     void trace_var(int traceMaskStackID, SlotRange r) {
692*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::trace_var, {r.index}, traceMaskStackID, r.count);
693*c8dee2aaSAndroid Build Coastguard Worker     }
694*c8dee2aaSAndroid Build Coastguard Worker 
695*c8dee2aaSAndroid Build Coastguard Worker     // Writes a variable update (via indirection) to the debug trace.
696*c8dee2aaSAndroid Build Coastguard Worker     void trace_var_indirect(int traceMaskStackID, SlotRange fixedRange,
697*c8dee2aaSAndroid Build Coastguard Worker                             int dynamicStackID, SlotRange limitRange);
698*c8dee2aaSAndroid Build Coastguard Worker 
699*c8dee2aaSAndroid Build Coastguard Worker     // Writes a function-entrance to the debug trace.
trace_enter(int traceMaskStackID,int funcID)700*c8dee2aaSAndroid Build Coastguard Worker     void trace_enter(int traceMaskStackID, int funcID) {
701*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::trace_enter, {}, traceMaskStackID, funcID);
702*c8dee2aaSAndroid Build Coastguard Worker     }
703*c8dee2aaSAndroid Build Coastguard Worker 
704*c8dee2aaSAndroid Build Coastguard Worker     // Writes a function-exit to the debug trace.
trace_exit(int traceMaskStackID,int funcID)705*c8dee2aaSAndroid Build Coastguard Worker     void trace_exit(int traceMaskStackID, int funcID) {
706*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::trace_exit, {}, traceMaskStackID, funcID);
707*c8dee2aaSAndroid Build Coastguard Worker     }
708*c8dee2aaSAndroid Build Coastguard Worker 
709*c8dee2aaSAndroid Build Coastguard Worker     // Writes a scope-level change to the debug trace.
trace_scope(int traceMaskStackID,int delta)710*c8dee2aaSAndroid Build Coastguard Worker     void trace_scope(int traceMaskStackID, int delta) {
711*c8dee2aaSAndroid Build Coastguard Worker         this->appendInstruction(BuilderOp::trace_scope, {}, traceMaskStackID, delta);
712*c8dee2aaSAndroid Build Coastguard Worker     }
713*c8dee2aaSAndroid Build Coastguard Worker 
714*c8dee2aaSAndroid Build Coastguard Worker private:
715*c8dee2aaSAndroid Build Coastguard Worker     struct SlotList {
fSlotASlotList716*c8dee2aaSAndroid Build Coastguard Worker         SlotList(Slot a = NA, Slot b = NA) : fSlotA(a), fSlotB(b) {}
717*c8dee2aaSAndroid Build Coastguard Worker         Slot fSlotA = NA;
718*c8dee2aaSAndroid Build Coastguard Worker         Slot fSlotB = NA;
719*c8dee2aaSAndroid Build Coastguard Worker     };
720*c8dee2aaSAndroid Build Coastguard Worker     void appendInstruction(BuilderOp op, SlotList slots,
721*c8dee2aaSAndroid Build Coastguard Worker                            int a = 0, int b = 0, int c = 0, int d = 0);
722*c8dee2aaSAndroid Build Coastguard Worker     Instruction* lastInstruction(int fromBack = 0);
723*c8dee2aaSAndroid Build Coastguard Worker     Instruction* lastInstructionOnAnyStack(int fromBack = 0);
724*c8dee2aaSAndroid Build Coastguard Worker     void simplifyPopSlotsUnmasked(SlotRange* dst);
725*c8dee2aaSAndroid Build Coastguard Worker     bool simplifyImmediateUnmaskedOp();
726*c8dee2aaSAndroid Build Coastguard Worker 
727*c8dee2aaSAndroid Build Coastguard Worker     skia_private::TArray<Instruction> fInstructions;
728*c8dee2aaSAndroid Build Coastguard Worker     int fNumLabels = 0;
729*c8dee2aaSAndroid Build Coastguard Worker     int fExecutionMaskWritesEnabled = 0;
730*c8dee2aaSAndroid Build Coastguard Worker     int fCurrentStackID = 0;
731*c8dee2aaSAndroid Build Coastguard Worker };
732*c8dee2aaSAndroid Build Coastguard Worker 
733*c8dee2aaSAndroid Build Coastguard Worker }  // namespace RP
734*c8dee2aaSAndroid Build Coastguard Worker }  // namespace SkSL
735*c8dee2aaSAndroid Build Coastguard Worker 
736*c8dee2aaSAndroid Build Coastguard Worker #endif  // SKSL_RASTERPIPELINEBUILDER
737