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