1/*
2 * Copyright 2019 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SKSL_BYTECODE
9#define SKSL_BYTECODE
10
11#include "include/private/SkOnce.h"
12#include "src/sksl/SkSLString.h"
13
14#include <memory>
15#include <vector>
16
17namespace SkSL {
18
19class ExternalValue;
20struct FunctionDeclaration;
21
22enum class ByteCodeInstruction : uint8_t {
23 // B = bool, F = float, I = int, S = signed, U = unsigned
24
25 kAddF, // N
26 kAddI, // N
27 kAndB, // N
28 kATan, // N
29 kBranch,
30 // Followed by a byte indicating the index of the function to call
31 kCall,
32 // Followed by three bytes indicating: the number of argument slots, the number of return slots,
33 // and the index of the external value to call
34 kCallExternal,
35 kCeil, // N
36 // For dynamic array access: Followed by byte indicating length of array
37 kClampIndex,
38 kCompareIEQ, // N
39 kCompareINEQ, // N
40 kCompareFEQ, // N
41 kCompareFNEQ, // N
42 kCompareFGT, // N
43 kCompareFGTEQ, // N
44 kCompareFLT, // N
45 kCompareFLTEQ, // N
46 kCompareSGT, // N
47 kCompareSGTEQ, // N
48 kCompareSLT, // N
49 kCompareSLTEQ, // N
50 kCompareUGT, // N
51 kCompareUGTEQ, // N
52 kCompareULT, // N
53 kCompareULTEQ, // N
54 kConvertFtoI, // N
55 kConvertStoF, // N
56 kConvertUtoF, // N
57 kCos, // N
58 kDivideF, // N
59 kDivideS, // N
60 kDivideU, // N
61 // Duplicates the top N stack values
62 kDup, // N
63 kFloor, // N
64 kFract, // N
65 kInverse2x2,
66 kInverse3x3,
67 kInverse4x4,
68 // A1, A2, .., B1, B2, .., T1, T2, .. -> lerp(A1, B1, T1), lerp(A2, B2, T2), ..
69 kLerp, // N
70 kLoad, // N, slot
71 kLoadGlobal, // N, slot
72 kLoadUniform, // N, slot
73 // Indirect loads get the slot to load from the top of the stack
74 kLoadExtended, // N
75 kLoadExtendedGlobal, // N
76 kLoadExtendedUniform, // N
77 // Loads "sk_FragCoord" [X, Y, Z, 1/W]
78 kLoadFragCoord,
79 // Followed by four bytes: srcCols, srcRows, dstCols, dstRows. Consumes the src matrix from the
80 // stack, and replaces it with the dst matrix. Per GLSL rules, there are no restrictions on
81 // dimensions. Any overlapping values are copied, and any other values are filled in with the
82 // identity matrix.
83 kMatrixToMatrix,
84 // Followed by three bytes: leftCols (== rightRows), leftRows, rightCols
85 kMatrixMultiply,
86 kMaxF, // N
87 kMaxS, // N -- SkSL only declares signed versions of min/max
88 kMinF, // N
89 kMinS, // N
90 // Masked selection: Stack is ... A1, A2, A3, B1, B2, B3, M1, M2, M3
91 // Result: M1 ? B1 : A1, M2 ? B2 : A2, M3 ? B3 : A3
92 kMix, // N
93 kNegateF, // N
94 kNegateI, // N
95 kMultiplyF, // N
96 kMultiplyI, // N
97 kNotB, // N
98 kOrB, // N
99 kPop, // N
100 kPow, // N
101 // Followed by a 32 bit value containing the value to push
102 kPushImmediate,
103 kReadExternal, // N, slot
104 kRemainderF, // N
105 kRemainderS, // N
106 kRemainderU, // N
107 // Followed by a byte indicating the number of slots to reserve on the stack (for later return)
108 kReserve,
109 // Followed by a byte indicating the number of slots being returned
110 kReturn,
111 // kSample* are followed by a byte indicating the FP slot to sample, and produce (R, G, B, A)
112 // Does "pass-through" sampling at the same coords as the parent
113 kSample,
114 // Expects stack to contain (X, Y)
115 kSampleExplicit,
116 // Expects stack to contain a 3x3 matrix (applied to parent's sample coords)
117 kSampleMatrix,
118 // Followed by two bytes indicating columns and rows of matrix (2, 3, or 4 each).
119 // Takes a single value from the top of the stack, and converts to a CxR matrix with that value
120 // replicated along the diagonal (and zero elsewhere), per the GLSL matrix construction rules.
121 kScalarToMatrix,
122 // Followed by a byte indicating the number of bits to shift
123 kShiftLeft,
124 kShiftRightS,
125 kShiftRightU,
126 kSin, // N
127 kSqrt, // N
128 kStore, // N, slot
129 kStoreGlobal, // N, slot
130 // Indirect stores get the slot to store from the top of the stack
131 kStoreExtended, // N
132 kStoreExtendedGlobal, // N
133 // Followed by two count bytes (1-4), and then one byte per swizzle component (0-3). The first
134 // count byte provides the current vector size (the vector is the top n stack elements), and the
135 // second count byte provides the swizzle component count.
136 kSwizzle,
137 kSubtractF, // N
138 kSubtractI, // N
139 kTan, // N
140 kWriteExternal, // N, slot
141 kXorB, // N
142
143 kMaskPush,
144 kMaskPop,
145 kMaskNegate,
146 // Followed by count byte
147 kMaskBlend,
148 // Followed by address
149 kBranchIfAllFalse,
150
151 kLoopBegin,
152 kLoopNext,
153 kLoopMask,
154 kLoopEnd,
155 kLoopBreak,
156 kLoopContinue,
157};
158
159class ByteCodeFunction {
160public:
161 int getParameterCount() const { return fParameterCount; }
162 int getReturnCount() const { return fReturnCount; }
163 int getLocalCount() const { return fLocalCount; }
164
165 const uint8_t* code() const { return fCode.data(); }
166 size_t size() const { return fCode.size(); }
167
168 /**
169 * Print bytecode disassembly to stdout.
170 */
171 void disassemble() const;
172
173private:
174 ByteCodeFunction(const FunctionDeclaration* declaration);
175
176 friend class ByteCode;
177 friend class ByteCodeGenerator;
178 friend struct Interpreter;
179
180 struct Parameter {
181 int fSlotCount;
182 bool fIsOutParameter;
183 };
184
185 SkSL::String fName;
186 std::vector<Parameter> fParameters;
187 int fParameterCount;
188 int fReturnCount = 0;
189
190 int fLocalCount = 0;
191 int fStackCount = 0;
192 int fConditionCount = 0;
193 int fLoopCount = 0;
194 std::vector<uint8_t> fCode;
195};
196
197enum class TypeCategory {
198 kBool,
199 kSigned,
200 kUnsigned,
201 kFloat,
202};
203
204class SK_API ByteCode {
205public:
206 static constexpr int kVecWidth = 8;
207
208 ByteCode() = default;
209
210 const ByteCodeFunction* getFunction(const char* name) const {
211 for (const auto& f : fFunctions) {
212 if (f->fName == name) {
213 return f.get();
214 }
215 }
216 return nullptr;
217 }
218
219 /**
220 * Invokes the specified function once, with the given arguments.
221 * 'args', 'outReturn', and 'uniforms' are collections of 32-bit values (typically floats,
222 * but possibly int32_t or uint32_t, depending on the types used in the SkSL).
223 * Any 'out' or 'inout' parameters will result in the 'args' array being modified.
224 * The return value is stored in 'outReturn' (may be null, to discard the return value).
225 * 'uniforms' are mapped to 'uniform' globals, in order.
226 */
227 bool SKSL_WARN_UNUSED_RESULT run(const ByteCodeFunction*,
228 float* args, int argCount,
229 float* outReturn, int returnCount,
230 const float* uniforms, int uniformCount) const;
231
232 /**
233 * Invokes the specified function with the given arguments, 'N' times. 'args' and 'outReturn'
234 * are accepted and returned in structure-of-arrays form:
235 * args[0] points to an array of N values, the first argument for each invocation
236 * ...
237 * args[argCount - 1] points to an array of N values, the last argument for each invocation
238 *
239 * All values in 'args', 'outReturn', and 'uniforms' are 32-bit values (typically floats,
240 * but possibly int32_t or uint32_t, depending on the types used in the SkSL).
241 * Any 'out' or 'inout' parameters will result in the 'args' array being modified.
242 * The return value is stored in 'outReturn' (may be null, to discard the return value).
243 * 'uniforms' are mapped to 'uniform' globals, in order.
244 */
245 bool SKSL_WARN_UNUSED_RESULT runStriped(const ByteCodeFunction*, int N,
246 float* args[], int argCount,
247 float* outReturn[], int returnCount,
248 const float* uniforms, int uniformCount) const;
249
250 struct Uniform {
251 SkSL::String fName;
252 TypeCategory fType;
253 int fColumns;
254 int fRows;
255 int fSlot;
256 };
257
258 int getUniformSlotCount() const { return fUniformSlotCount; }
259 int getUniformCount() const { return fUniforms.size(); }
260 int getUniformLocation(const char* name) const {
261 for (int i = 0; i < (int)fUniforms.size(); ++i) {
262 if (fUniforms[i].fName == name) {
263 return fUniforms[i].fSlot;
264 }
265 }
266 return -1;
267 }
268 const Uniform& getUniform(int i) const { return fUniforms[i]; }
269
270 /**
271 * Some byte code programs can't be executed by the interpreter, due to unsupported features.
272 * They may still be used to convert to other formats, or for reflection of uniforms.
273 */
274 bool canRun() const { return fChildFPCount == 0 && !fUsesFragCoord; }
275
276private:
277 ByteCode(const ByteCode&) = delete;
278 ByteCode& operator=(const ByteCode&) = delete;
279
280 friend class ByteCodeGenerator;
281 friend struct Interpreter;
282
283 int fGlobalSlotCount = 0;
284 int fUniformSlotCount = 0;
285 int fChildFPCount = 0;
286 bool fUsesFragCoord = false;
287 std::vector<Uniform> fUniforms;
288
289 std::vector<std::unique_ptr<ByteCodeFunction>> fFunctions;
290 std::vector<ExternalValue*> fExternalValues;
291};
292
293} // namespace SkSL
294
295#endif
296