Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 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 | |
Brian Osman | 8016441 | 2019-06-07 13:00:23 -0400 | [diff] [blame] | 11 | #include "src/sksl/SkSLString.h" |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 12 | |
Ethan Nicholas | 91164d1 | 2019-05-15 15:29:54 -0400 | [diff] [blame] | 13 | #include <memory> |
| 14 | #include <vector> |
| 15 | |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 16 | namespace SkSL { |
| 17 | |
Ethan Nicholas | 91164d1 | 2019-05-15 15:29:54 -0400 | [diff] [blame] | 18 | class ExternalValue; |
| 19 | struct FunctionDeclaration; |
| 20 | |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 21 | #define VECTOR(name) name, name ## 2, name ## 3, name ## 4 |
Brian Osman | 1e855b2 | 2019-05-29 15:21:52 -0400 | [diff] [blame] | 22 | #define VECTOR_MATRIX(name) name, name ## 2, name ## 3, name ## 4, name ## N |
| 23 | |
Mike Klein | 108e935 | 2019-05-21 11:05:17 -0500 | [diff] [blame] | 24 | enum class ByteCodeInstruction : uint16_t { |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 25 | // B = bool, F = float, I = int, S = signed, U = unsigned |
Brian Osman | 1e855b2 | 2019-05-29 15:21:52 -0400 | [diff] [blame] | 26 | VECTOR_MATRIX(kAddF), |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 27 | VECTOR(kAddI), |
Brian Osman | 32c526b | 2019-06-03 16:13:52 -0400 | [diff] [blame] | 28 | kAndB, |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 29 | kBranch, |
Brian Osman | 226668a | 2019-05-14 16:47:30 -0400 | [diff] [blame] | 30 | // Followed by a byte indicating the index of the function to call |
| 31 | kCall, |
Ethan Nicholas | 9e6a393 | 2019-05-17 16:31:21 -0400 | [diff] [blame] | 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, |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 35 | VECTOR(kCompareIEQ), |
| 36 | VECTOR(kCompareINEQ), |
Brian Osman | 1e855b2 | 2019-05-29 15:21:52 -0400 | [diff] [blame] | 37 | VECTOR_MATRIX(kCompareFEQ), |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 38 | VECTOR(kCompareFGT), |
| 39 | VECTOR(kCompareFGTEQ), |
| 40 | VECTOR(kCompareFLT), |
| 41 | VECTOR(kCompareFLTEQ), |
Brian Osman | 1e855b2 | 2019-05-29 15:21:52 -0400 | [diff] [blame] | 42 | VECTOR_MATRIX(kCompareFNEQ), |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 43 | VECTOR(kCompareSGT), |
| 44 | VECTOR(kCompareSGTEQ), |
| 45 | VECTOR(kCompareSLT), |
| 46 | VECTOR(kCompareSLTEQ), |
| 47 | VECTOR(kCompareUGT), |
| 48 | VECTOR(kCompareUGTEQ), |
| 49 | VECTOR(kCompareULT), |
| 50 | VECTOR(kCompareULTEQ), |
Ethan Nicholas | 82162ee | 2019-05-21 16:05:08 -0400 | [diff] [blame] | 51 | VECTOR(kConvertFtoI), |
| 52 | VECTOR(kConvertStoF), |
| 53 | VECTOR(kConvertUtoF), |
| 54 | VECTOR(kCos), |
Ethan Nicholas | ae9633b | 2019-05-24 12:46:34 -0400 | [diff] [blame] | 55 | kCross, |
Brian Osman | 1e855b2 | 2019-05-29 15:21:52 -0400 | [diff] [blame] | 56 | VECTOR_MATRIX(kDivideF), |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 57 | VECTOR(kDivideS), |
| 58 | VECTOR(kDivideU), |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 59 | // Duplicates the top stack value |
Brian Osman | 1e855b2 | 2019-05-29 15:21:52 -0400 | [diff] [blame] | 60 | VECTOR_MATRIX(kDup), |
Brian Osman | 07c117b | 2019-05-23 12:51:06 -0700 | [diff] [blame] | 61 | // kLoad/kLoadGlobal are followed by a byte indicating the local/global slot to load |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 62 | VECTOR(kLoad), |
| 63 | VECTOR(kLoadGlobal), |
Brian Osman | 07c117b | 2019-05-23 12:51:06 -0700 | [diff] [blame] | 64 | // As kLoad/kLoadGlobal, then a count byte (1-4), and then one byte per swizzle component (0-3). |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 65 | kLoadSwizzle, |
Brian Osman | b745129 | 2019-05-15 13:02:13 -0400 | [diff] [blame] | 66 | kLoadSwizzleGlobal, |
Brian Osman | 07c117b | 2019-05-23 12:51:06 -0700 | [diff] [blame] | 67 | // kLoadExtended* are fallback load ops when we lack a specialization. They are followed by a |
| 68 | // count byte, and get the slot to load from the top of the stack. |
| 69 | kLoadExtended, |
| 70 | kLoadExtendedGlobal, |
Brian Osman | 29e013d | 2019-05-28 17:16:03 -0400 | [diff] [blame] | 71 | // Followed by four bytes: srcCols, srcRows, dstCols, dstRows. Consumes the src matrix from the |
| 72 | // stack, and replaces it with the dst matrix. Per GLSL rules, there are no restrictions on |
| 73 | // dimensions. Any overlapping values are copied, and any other values are filled in with the |
| 74 | // identity matrix. |
| 75 | kMatrixToMatrix, |
Brian Osman | 909231c | 2019-05-29 15:34:36 -0400 | [diff] [blame] | 76 | // Followed by three bytes: leftCols (== rightRows), leftRows, rightCols |
| 77 | kMatrixMultiply, |
Brian Osman | 1e855b2 | 2019-05-29 15:21:52 -0400 | [diff] [blame] | 78 | VECTOR_MATRIX(kNegateF), |
Mike Klein | 1271091 | 2019-05-21 11:04:59 -0500 | [diff] [blame] | 79 | VECTOR(kNegateI), |
Ethan Nicholas | ae9633b | 2019-05-24 12:46:34 -0400 | [diff] [blame] | 80 | VECTOR(kMix), |
Brian Osman | 1e855b2 | 2019-05-29 15:21:52 -0400 | [diff] [blame] | 81 | VECTOR_MATRIX(kMultiplyF), |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 82 | VECTOR(kMultiplyI), |
Brian Osman | 569f12f | 2019-06-13 11:23:57 -0400 | [diff] [blame] | 83 | kNotB, |
Brian Osman | 32c526b | 2019-06-03 16:13:52 -0400 | [diff] [blame] | 84 | kOrB, |
Brian Osman | 1e855b2 | 2019-05-29 15:21:52 -0400 | [diff] [blame] | 85 | VECTOR_MATRIX(kPop), |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 86 | // Followed by a 32 bit value containing the value to push |
| 87 | kPushImmediate, |
Ethan Nicholas | 91164d1 | 2019-05-15 15:29:54 -0400 | [diff] [blame] | 88 | // Followed by a byte indicating external value to read |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 89 | VECTOR(kReadExternal), |
| 90 | VECTOR(kRemainderF), |
| 91 | VECTOR(kRemainderS), |
| 92 | VECTOR(kRemainderU), |
Ethan Nicholas | 746035a | 2019-04-23 13:31:09 -0400 | [diff] [blame] | 93 | // Followed by a byte indicating the number of slots being returned |
| 94 | kReturn, |
Brian Osman | 29e013d | 2019-05-28 17:16:03 -0400 | [diff] [blame] | 95 | // Followed by two bytes indicating columns and rows of matrix (2, 3, or 4 each). |
| 96 | // Takes a single value from the top of the stack, and converts to a CxR matrix with that value |
| 97 | // replicated along the diagonal (and zero elsewhere), per the GLSL matrix construction rules. |
| 98 | kScalarToMatrix, |
Ethan Nicholas | 82162ee | 2019-05-21 16:05:08 -0400 | [diff] [blame] | 99 | VECTOR(kSin), |
| 100 | VECTOR(kSqrt), |
Brian Osman | 07c117b | 2019-05-23 12:51:06 -0700 | [diff] [blame] | 101 | // kStore/kStoreGlobal are followed by a byte indicating the local/global slot to store |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 102 | VECTOR(kStore), |
| 103 | VECTOR(kStoreGlobal), |
Brian Osman | 07c117b | 2019-05-23 12:51:06 -0700 | [diff] [blame] | 104 | // Fallback stores. Followed by count byte, and get the slot to store from the top of the stack |
| 105 | kStoreExtended, |
| 106 | kStoreExtendedGlobal, |
| 107 | // As kStore/kStoreGlobal, then a count byte (1-4), then one byte per swizzle component (0-3). |
Brian Osman | 1091f02 | 2019-05-16 09:42:16 -0400 | [diff] [blame] | 108 | // Expects the stack to look like: ... v1 v2 v3 v4, where the number of 'v's is equal to the |
| 109 | // number of swizzle components. After the store, all v's are popped from the stack. |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 110 | kStoreSwizzle, |
Brian Osman | 1091f02 | 2019-05-16 09:42:16 -0400 | [diff] [blame] | 111 | kStoreSwizzleGlobal, |
Brian Osman | 07c117b | 2019-05-23 12:51:06 -0700 | [diff] [blame] | 112 | // As above, but gets the store slot from the top of the stack (before values to be stored) |
| 113 | kStoreSwizzleIndirect, |
| 114 | kStoreSwizzleIndirectGlobal, |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 115 | // Followed by two count bytes (1-4), and then one byte per swizzle component (0-3). The first |
| 116 | // count byte provides the current vector size (the vector is the top n stack elements), and the |
| 117 | // second count byte provides the swizzle component count. |
| 118 | kSwizzle, |
Brian Osman | 1e855b2 | 2019-05-29 15:21:52 -0400 | [diff] [blame] | 119 | VECTOR_MATRIX(kSubtractF), |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 120 | VECTOR(kSubtractI), |
Ethan Nicholas | 82162ee | 2019-05-21 16:05:08 -0400 | [diff] [blame] | 121 | VECTOR(kTan), |
Ethan Nicholas | 91164d1 | 2019-05-15 15:29:54 -0400 | [diff] [blame] | 122 | // Followed by a byte indicating external value to write |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 123 | VECTOR(kWriteExternal), |
Brian Osman | 569f12f | 2019-06-13 11:23:57 -0400 | [diff] [blame] | 124 | kXorB, |
| 125 | |
| 126 | kMaskPush, |
| 127 | kMaskPop, |
| 128 | kMaskNegate, |
| 129 | // Followed by count byte |
| 130 | kMaskBlend, |
| 131 | // Followed by address |
| 132 | kBranchIfAllFalse, |
| 133 | |
| 134 | kLoopBegin, |
| 135 | kLoopNext, |
| 136 | kLoopMask, |
| 137 | kLoopEnd, |
| 138 | kLoopBreak, |
| 139 | kLoopContinue, |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 140 | }; |
Ethan Nicholas | 48a75aa | 2019-05-16 17:15:56 -0400 | [diff] [blame] | 141 | #undef VECTOR |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 142 | |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 143 | struct ByteCodeFunction { |
Brian Osman | 8016441 | 2019-06-07 13:00:23 -0400 | [diff] [blame] | 144 | ByteCodeFunction(const FunctionDeclaration* declaration); |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 145 | |
Brian Osman | 8016441 | 2019-06-07 13:00:23 -0400 | [diff] [blame] | 146 | struct Parameter { |
| 147 | int fSlotCount; |
| 148 | bool fIsOutParameter; |
| 149 | }; |
| 150 | |
| 151 | SkSL::String fName; |
| 152 | std::vector<Parameter> fParameters; |
| 153 | int fParameterCount; |
| 154 | |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 155 | int fLocalCount = 0; |
Ethan Nicholas | dfcad06 | 2019-05-07 12:53:34 -0400 | [diff] [blame] | 156 | // TODO: Compute this value analytically. For now, just pick an arbitrary value that we probably |
| 157 | // won't overflow. |
| 158 | int fStackCount = 128; |
| 159 | int fReturnCount = 0; |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 160 | std::vector<uint8_t> fCode; |
Brian Osman | 08a8496 | 2019-06-14 10:17:16 -0400 | [diff] [blame^] | 161 | |
| 162 | /** |
| 163 | * Print bytecode disassembly to stdout. |
| 164 | */ |
| 165 | void disassemble() const; |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 166 | }; |
| 167 | |
| 168 | struct ByteCode { |
| 169 | int fGlobalCount = 0; |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 170 | // one entry per input slot, contains the global slot to which the input slot maps |
| 171 | std::vector<uint8_t> fInputSlots; |
| 172 | std::vector<std::unique_ptr<ByteCodeFunction>> fFunctions; |
Brian Osman | 08a8496 | 2019-06-14 10:17:16 -0400 | [diff] [blame^] | 173 | std::vector<ExternalValue*> fExternalValues; |
Brian Osman | 226668a | 2019-05-14 16:47:30 -0400 | [diff] [blame] | 174 | |
| 175 | const ByteCodeFunction* getFunction(const char* name) const { |
| 176 | for (const auto& f : fFunctions) { |
Brian Osman | 8016441 | 2019-06-07 13:00:23 -0400 | [diff] [blame] | 177 | if (f->fName == name) { |
Brian Osman | 226668a | 2019-05-14 16:47:30 -0400 | [diff] [blame] | 178 | return f.get(); |
| 179 | } |
| 180 | } |
| 181 | return nullptr; |
| 182 | } |
Ethan Nicholas | 91164d1 | 2019-05-15 15:29:54 -0400 | [diff] [blame] | 183 | |
Brian Osman | 08a8496 | 2019-06-14 10:17:16 -0400 | [diff] [blame^] | 184 | /** |
| 185 | * Invokes the specified function with the given arguments, 'N' times. |
| 186 | * 'args', 'outReturn', and 'uniforms' are collections of 32-bit values (typically floats, |
| 187 | * but possibly int32_t or uint32_t, depending on the types used in the SkSL). |
| 188 | * Any 'out' or 'inout' parameters will result in the 'args' array being modified. |
| 189 | * The return value is stored in 'outReturn' (may be null, to discard the return value). |
| 190 | * 'uniforms' are mapped to 'uniform' globals, in order. |
| 191 | */ |
| 192 | void run(const ByteCodeFunction*, float* args, float* outReturn, int N, |
| 193 | const float* uniforms, int uniformCount) const; |
Ethan Nicholas | 0e9401d | 2019-03-21 11:05:37 -0400 | [diff] [blame] | 194 | }; |
| 195 | |
| 196 | } |
| 197 | |
| 198 | #endif |