1 /*
2 * Copyright 2020 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 #include "include/private/SkSLProgramElement.h"
9 #include "include/private/SkSLStatement.h"
10 #include "include/private/SkTArray.h"
11 #include "include/private/SkTPin.h"
12 #include "src/sksl/SkSLCompiler.h"
13 #include "src/sksl/SkSLOperators.h"
14 #include "src/sksl/codegen/SkSLCodeGenerator.h"
15 #include "src/sksl/codegen/SkSLVMCodeGenerator.h"
16 #include "src/sksl/ir/SkSLBinaryExpression.h"
17 #include "src/sksl/ir/SkSLBlock.h"
18 #include "src/sksl/ir/SkSLBreakStatement.h"
19 #include "src/sksl/ir/SkSLChildCall.h"
20 #include "src/sksl/ir/SkSLConstructor.h"
21 #include "src/sksl/ir/SkSLConstructorArray.h"
22 #include "src/sksl/ir/SkSLConstructorArrayCast.h"
23 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
24 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
25 #include "src/sksl/ir/SkSLConstructorSplat.h"
26 #include "src/sksl/ir/SkSLConstructorStruct.h"
27 #include "src/sksl/ir/SkSLContinueStatement.h"
28 #include "src/sksl/ir/SkSLDoStatement.h"
29 #include "src/sksl/ir/SkSLExpressionStatement.h"
30 #include "src/sksl/ir/SkSLExternalFunctionCall.h"
31 #include "src/sksl/ir/SkSLExternalFunctionReference.h"
32 #include "src/sksl/ir/SkSLFieldAccess.h"
33 #include "src/sksl/ir/SkSLForStatement.h"
34 #include "src/sksl/ir/SkSLFunctionCall.h"
35 #include "src/sksl/ir/SkSLFunctionDeclaration.h"
36 #include "src/sksl/ir/SkSLFunctionDefinition.h"
37 #include "src/sksl/ir/SkSLIfStatement.h"
38 #include "src/sksl/ir/SkSLIndexExpression.h"
39 #include "src/sksl/ir/SkSLLiteral.h"
40 #include "src/sksl/ir/SkSLPostfixExpression.h"
41 #include "src/sksl/ir/SkSLPrefixExpression.h"
42 #include "src/sksl/ir/SkSLReturnStatement.h"
43 #include "src/sksl/ir/SkSLSwitchStatement.h"
44 #include "src/sksl/ir/SkSLSwizzle.h"
45 #include "src/sksl/ir/SkSLTernaryExpression.h"
46 #include "src/sksl/ir/SkSLVarDeclarations.h"
47 #include "src/sksl/ir/SkSLVariableReference.h"
48 #include "src/sksl/tracing/SkVMDebugTrace.h"
49
50 #include <algorithm>
51 #include <unordered_map>
52
53 namespace {
54 // sksl allows the optimizations of fast_mul(), so we want to use that most of the time.
55 // This little sneaky snippet of code lets us use ** as a fast multiply infix operator.
56 struct FastF32 { skvm::F32 val; };
operator *(skvm::F32 y)57 static FastF32 operator*(skvm::F32 y) { return {y}; }
operator *(skvm::F32 x,FastF32 y)58 static skvm::F32 operator*(skvm::F32 x, FastF32 y) { return fast_mul(x, y.val); }
operator *(float x,FastF32 y)59 static skvm::F32 operator*(float x, FastF32 y) { return fast_mul(x, y.val); }
60
61 class SkSLTracer : public skvm::TraceHook {
62 public:
Make(SkSL::SkVMDebugTrace * trace)63 static std::unique_ptr<SkSLTracer> Make(SkSL::SkVMDebugTrace* trace) {
64 auto hook = std::make_unique<SkSLTracer>();
65 hook->fTrace = trace;
66 return hook;
67 }
68
line(int lineNum)69 void line(int lineNum) override {
70 fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kLine,
71 /*data=*/{lineNum, 0}});
72 }
var(int slot,int32_t val)73 void var(int slot, int32_t val) override {
74 fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kVar,
75 /*data=*/{slot, val}});
76 }
enter(int fnIdx)77 void enter(int fnIdx) override {
78 fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kEnter,
79 /*data=*/{fnIdx, 0}});
80 }
exit(int fnIdx)81 void exit(int fnIdx) override {
82 fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kExit,
83 /*data=*/{fnIdx, 0}});
84 }
scope(int delta)85 void scope(int delta) override {
86 fTrace->fTraceInfo.push_back({SkSL::SkVMTraceInfo::Op::kScope,
87 /*data=*/{delta, 0}});
88 }
89
90 private:
91 SkSL::SkVMDebugTrace* fTrace;
92 };
93 } // namespace
94
95 namespace SkSL {
96
97 namespace {
98
99 // Holds scalars, vectors, or matrices
100 struct Value {
101 Value() = default;
ValueSkSL::__anonf8b5168e0211::Value102 explicit Value(size_t slots) {
103 fVals.resize(slots);
104 }
ValueSkSL::__anonf8b5168e0211::Value105 Value(skvm::F32 x) : fVals({ x.id }) {}
ValueSkSL::__anonf8b5168e0211::Value106 Value(skvm::I32 x) : fVals({ x.id }) {}
107
operator boolSkSL::__anonf8b5168e0211::Value108 explicit operator bool() const { return !fVals.empty(); }
109
slotsSkSL::__anonf8b5168e0211::Value110 size_t slots() const { return fVals.size(); }
111
112 struct ValRef {
ValRefSkSL::__anonf8b5168e0211::Value::ValRef113 ValRef(skvm::Val& val) : fVal(val) {}
114
operator =SkSL::__anonf8b5168e0211::Value::ValRef115 ValRef& operator=(ValRef v) { fVal = v.fVal; return *this; }
operator =SkSL::__anonf8b5168e0211::Value::ValRef116 ValRef& operator=(skvm::Val v) { fVal = v; return *this; }
operator =SkSL::__anonf8b5168e0211::Value::ValRef117 ValRef& operator=(skvm::F32 v) { fVal = v.id; return *this; }
operator =SkSL::__anonf8b5168e0211::Value::ValRef118 ValRef& operator=(skvm::I32 v) { fVal = v.id; return *this; }
119
operator skvm::ValSkSL::__anonf8b5168e0211::Value::ValRef120 operator skvm::Val() { return fVal; }
121
122 skvm::Val& fVal;
123 };
124
operator []SkSL::__anonf8b5168e0211::Value125 ValRef operator[](size_t i) {
126 // These redundant asserts work around what we think is a codegen bug in GCC 8.x for
127 // 32-bit x86 Debug builds.
128 SkASSERT(i < fVals.size());
129 return fVals[i];
130 }
operator []SkSL::__anonf8b5168e0211::Value131 skvm::Val operator[](size_t i) const {
132 // These redundant asserts work around what we think is a codegen bug in GCC 8.x for
133 // 32-bit x86 Debug builds.
134 SkASSERT(i < fVals.size());
135 return fVals[i];
136 }
137
asSpanSkSL::__anonf8b5168e0211::Value138 SkSpan<skvm::Val> asSpan() { return SkMakeSpan(fVals); }
139
140 private:
141 SkSTArray<4, skvm::Val, true> fVals;
142 };
143
144 } // namespace
145
146 class SkVMGenerator {
147 public:
148 SkVMGenerator(const Program& program,
149 skvm::Builder* builder,
150 SkVMDebugTrace* debugTrace,
151 SkVMCallbacks* callbacks);
152
153 void writeProgram(SkSpan<skvm::Val> uniforms,
154 skvm::Coord device,
155 const FunctionDefinition& function,
156 SkSpan<skvm::Val> arguments,
157 SkSpan<skvm::Val> outReturn);
158
159 private:
160 /**
161 * In SkSL, a Variable represents a named, typed value (along with qualifiers, etc).
162 * Every Variable is mapped to one (or several, contiguous) indices into our vector of
163 * skvm::Val. Those skvm::Val entries hold the current actual value of that variable.
164 *
165 * NOTE: Conceptually, each Variable is just mapped to a Value. We could implement it that way,
166 * (and eliminate the indirection), but it would add overhead for each Variable,
167 * and add additional (different) bookkeeping for things like lvalue-swizzles.
168 *
169 * Any time a variable appears in an expression, that's a VariableReference, which is a kind of
170 * Expression. Evaluating that VariableReference (or any other Expression) produces a Value,
171 * which is a set of skvm::Val. (This allows an Expression to produce a vector or matrix, in
172 * addition to a scalar).
173 *
174 * For a VariableReference, producing a Value is straightforward - we get the slot of the
175 * Variable (from fVariableMap), use that to look up the current skvm::Vals holding the
176 * variable's contents, and construct a Value with those ids.
177 */
178
179 /** Creates a Value from a collection of adjacent slots. */
180 Value getSlotValue(size_t slot, size_t nslots);
181
182 /**
183 * Returns the slot index of this function inside the SkVMFunctionInfo array in SkVMDebugTrace.
184 * The SkVMFunctionInfo slot will be created if it doesn't already exist.
185 */
186 int getDebugFunctionInfo(const FunctionDeclaration& decl);
187
188 /** Used by `createSlot` to add this variable to the SkVMSlotInfo array inside SkVMDebugTrace.*/
189 void addDebugSlotInfo(const std::string& varName, const Type& type, int line,
190 int fnReturnValue);
191
192 void addDebugSlotInfoForGroup(const std::string& varName, const Type& type, int line,
193 int* groupIndex, int fnReturnValue);
194
195 /** Used by `getSlot` to create a new slot on its first access. */
196 size_t createSlot(const std::string& name, const Type& type, int line, int fnReturnValue);
197
198 /**
199 * Returns the slot holding v's Val(s). Allocates storage if this is first time 'v' is
200 * referenced. Compound variables (e.g. vectors) will consume more than one slot, with
201 * getSlot returning the start of the contiguous chunk of slots.
202 */
203 size_t getSlot(const Variable& v);
204
205 /**
206 * Returns the slot holding fn's return value. Allocates storage if this is first time accessing
207 * the slot.
208 */
209 size_t getSlot(const FunctionDefinition& fn);
210
211 /**
212 * Writes a value to a slot previously created by getSlot.
213 */
214 void writeToSlot(int slot, skvm::Val value);
215
216 /**
217 * Emits an trace_line opcode. writeStatement does this, and statements that alter control flow
218 * may need to explicitly add additional traces.
219 */
220 void emitTraceLine(int line);
221
222 /** Emits an trace_scope opcode, which alters the SkSL variable-scope depth. */
223 void emitTraceScope(skvm::I32 executionMask, int delta);
224
225 /** Initializes uniforms and global variables at the start of main(). */
226 void setupGlobals(SkSpan<skvm::Val> uniforms, skvm::Coord device);
227
228 /** Emits an SkSL function. Returns the slot index of the SkSL function's return value. */
229 size_t writeFunction(const FunctionDefinition& function, SkSpan<skvm::Val> arguments);
230
f32(skvm::Val id)231 skvm::F32 f32(skvm::Val id) { SkASSERT(id != skvm::NA); return {fBuilder, id}; }
i32(skvm::Val id)232 skvm::I32 i32(skvm::Val id) { SkASSERT(id != skvm::NA); return {fBuilder, id}; }
233
234 // Shorthand for scalars
f32(const Value & v)235 skvm::F32 f32(const Value& v) { SkASSERT(v.slots() == 1); return f32(v[0]); }
i32(const Value & v)236 skvm::I32 i32(const Value& v) { SkASSERT(v.slots() == 1); return i32(v[0]); }
237
238 template <typename Fn>
unary(const Value & v,Fn && fn)239 Value unary(const Value& v, Fn&& fn) {
240 Value result(v.slots());
241 for (size_t i = 0; i < v.slots(); ++i) {
242 result[i] = fn({fBuilder, v[i]});
243 }
244 return result;
245 }
246
mask()247 skvm::I32 mask() {
248 // Mask off execution if we have encountered `break` or `continue` on this path.
249 skvm::I32 result = fConditionMask & fLoopMask;
250 if (!fFunctionStack.empty()) {
251 // As we encounter (possibly conditional) return statements, fReturned is updated to
252 // store the lanes that have already returned. For the remainder of the current
253 // function, those lanes should be disabled.
254 result = result & ~currentFunction().fReturned;
255 }
256 return result;
257 }
258
259 size_t fieldSlotOffset(const FieldAccess& expr);
260 size_t indexSlotOffset(const IndexExpression& expr);
261
262 Value writeExpression(const Expression& expr);
263 Value writeBinaryExpression(const BinaryExpression& b);
264 Value writeAggregationConstructor(const AnyConstructor& c);
265 Value writeChildCall(const ChildCall& c);
266 Value writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& c);
267 Value writeConstructorMatrixResize(const ConstructorMatrixResize& c);
268 Value writeConstructorCast(const AnyConstructor& c);
269 Value writeConstructorSplat(const ConstructorSplat& c);
270 Value writeFunctionCall(const FunctionCall& c);
271 Value writeExternalFunctionCall(const ExternalFunctionCall& c);
272 Value writeFieldAccess(const FieldAccess& expr);
273 Value writeLiteral(const Literal& l);
274 Value writeIndexExpression(const IndexExpression& expr);
275 Value writeIntrinsicCall(const FunctionCall& c);
276 Value writePostfixExpression(const PostfixExpression& p);
277 Value writePrefixExpression(const PrefixExpression& p);
278 Value writeSwizzle(const Swizzle& swizzle);
279 Value writeTernaryExpression(const TernaryExpression& t);
280 Value writeVariableExpression(const VariableReference& expr);
281
282 Value writeTypeConversion(const Value& src, Type::NumberKind srcKind, Type::NumberKind dstKind);
283
284 void writeStatement(const Statement& s);
285 void writeBlock(const Block& b);
286 void writeBreakStatement();
287 void writeContinueStatement();
288 void writeForStatement(const ForStatement& f);
289 void writeIfStatement(const IfStatement& stmt);
290 void writeReturnStatement(const ReturnStatement& r);
291 void writeSwitchStatement(const SwitchStatement& s);
292 void writeVarDeclaration(const VarDeclaration& decl);
293
294 Value writeStore(const Expression& lhs, const Value& rhs);
295 skvm::Val writeConditionalStore(skvm::Val lhs, skvm::Val rhs, skvm::I32 mask);
296
297 Value writeMatrixInverse2x2(const Value& m);
298 Value writeMatrixInverse3x3(const Value& m);
299 Value writeMatrixInverse4x4(const Value& m);
300
301 void recursiveBinaryCompare(const Value& lVal, const Type& lType,
302 const Value& rVal, const Type& rType,
303 size_t* slotOffset, Value* result,
304 const std::function <Value(skvm::F32 x, skvm::F32 y)>& float_comp,
305 const std::function <Value(skvm::I32 x, skvm::I32 y)>& int_comp);
306
307 //
308 // Global state for the lifetime of the generator:
309 //
310 const Program& fProgram;
311 skvm::Builder* fBuilder;
312 SkVMDebugTrace* fDebugTrace;
313 int fTraceHookID = -1;
314 SkVMCallbacks* fCallbacks;
315
316 struct Slot {
317 skvm::Val val;
318 bool writtenTo = false;
319 };
320 std::vector<Slot> fSlots;
321
322 // [Variable, first slot in fSlots]
323 std::unordered_map<const Variable*, size_t> fVariableMap;
324 // [Function, first slot in fSlots]
325 std::unordered_map<const FunctionDefinition*, size_t> fReturnValueMap;
326
327 // Debug trace mask (set to true when fTraceCoord matches device coordinates)
328 skvm::I32 fTraceMask;
329
330 // Conditional execution mask (managed by ScopedCondition, and tied to control-flow scopes)
331 skvm::I32 fConditionMask;
332
333 // Similar: loop execution masks. Each loop starts with all lanes active (fLoopMask).
334 // 'break' disables a lane in fLoopMask until the loop finishes
335 // 'continue' disables a lane in fLoopMask, and sets fContinueMask to be re-enabled on the next
336 // iteration
337 skvm::I32 fLoopMask;
338 skvm::I32 fContinueMask;
339
340 //
341 // State that's local to the generation of a single function:
342 //
343 struct Function {
344 size_t fReturnSlot;
345 skvm::I32 fReturned;
346 };
347 std::vector<Function> fFunctionStack;
currentFunction()348 Function& currentFunction() { return fFunctionStack.back(); }
349
350 class ScopedCondition {
351 public:
ScopedCondition(SkVMGenerator * generator,skvm::I32 mask)352 ScopedCondition(SkVMGenerator* generator, skvm::I32 mask)
353 : fGenerator(generator), fOldConditionMask(fGenerator->fConditionMask) {
354 fGenerator->fConditionMask &= mask;
355 }
356
~ScopedCondition()357 ~ScopedCondition() { fGenerator->fConditionMask = fOldConditionMask; }
358
359 private:
360 SkVMGenerator* fGenerator;
361 skvm::I32 fOldConditionMask;
362 };
363 };
364
base_number_kind(const Type & type)365 static Type::NumberKind base_number_kind(const Type& type) {
366 if (type.typeKind() == Type::TypeKind::kMatrix || type.typeKind() == Type::TypeKind::kVector) {
367 return base_number_kind(type.componentType());
368 }
369 return type.numberKind();
370 }
371
is_uniform(const SkSL::Variable & var)372 static inline bool is_uniform(const SkSL::Variable& var) {
373 return var.modifiers().fFlags & Modifiers::kUniform_Flag;
374 }
375
SkVMGenerator(const Program & program,skvm::Builder * builder,SkVMDebugTrace * debugTrace,SkVMCallbacks * callbacks)376 SkVMGenerator::SkVMGenerator(const Program& program,
377 skvm::Builder* builder,
378 SkVMDebugTrace* debugTrace,
379 SkVMCallbacks* callbacks)
380 : fProgram(program)
381 , fBuilder(builder)
382 , fDebugTrace(debugTrace)
383 , fCallbacks(callbacks) {}
384
writeProgram(SkSpan<skvm::Val> uniforms,skvm::Coord device,const FunctionDefinition & function,SkSpan<skvm::Val> arguments,SkSpan<skvm::Val> outReturn)385 void SkVMGenerator::writeProgram(SkSpan<skvm::Val> uniforms,
386 skvm::Coord device,
387 const FunctionDefinition& function,
388 SkSpan<skvm::Val> arguments,
389 SkSpan<skvm::Val> outReturn) {
390 fConditionMask = fLoopMask = fBuilder->splat(0xffff'ffff);
391
392 this->setupGlobals(uniforms, device);
393 size_t returnSlot = this->writeFunction(function, arguments);
394
395 // Copy the value from the return slot into outReturn.
396 SkASSERT(function.declaration().returnType().slotCount() == outReturn.size());
397 for (size_t i = 0; i < outReturn.size(); ++i) {
398 outReturn[i] = fSlots[returnSlot + i].val;
399 }
400 }
401
setupGlobals(SkSpan<skvm::Val> uniforms,skvm::Coord device)402 void SkVMGenerator::setupGlobals(SkSpan<skvm::Val> uniforms, skvm::Coord device) {
403 if (fDebugTrace) {
404 // Copy the program source into the debug info so that it will be written in the trace file.
405 fDebugTrace->setSource(*fProgram.fSource);
406
407 // Create a trace hook and attach it to the builder.
408 fDebugTrace->fTraceHook = SkSLTracer::Make(fDebugTrace);
409 fTraceHookID = fBuilder->attachTraceHook(fDebugTrace->fTraceHook.get());
410
411 // The SkVM blitter generates centered pixel coordinates. (0.5, 1.5, 2.5, 3.5, etc.)
412 // Add 0.5 to the requested trace coordinate to match this.
413 skvm::Coord traceCoord = {to_F32(fBuilder->splat(fDebugTrace->fTraceCoord.fX)) + 0.5f,
414 to_F32(fBuilder->splat(fDebugTrace->fTraceCoord.fY)) + 0.5f};
415
416 // If we are debugging, we need to create a trace mask. This will be true when the current
417 // device coordinates match the requested trace coordinates. We calculate each mask
418 // individually to guarantee consistent order-of-evaluation.
419 skvm::I32 xMask = (device.x == traceCoord.x),
420 yMask = (device.y == traceCoord.y);
421 fTraceMask = xMask & yMask;
422 }
423
424 // Add storage for each global variable (including uniforms) to fSlots, and entries in
425 // fVariableMap to remember where every variable is stored.
426 const skvm::Val* uniformIter = uniforms.begin();
427 size_t fpCount = 0;
428 for (const ProgramElement* e : fProgram.elements()) {
429 if (e->is<GlobalVarDeclaration>()) {
430 const GlobalVarDeclaration& gvd = e->as<GlobalVarDeclaration>();
431 const VarDeclaration& decl = gvd.declaration()->as<VarDeclaration>();
432 const Variable& var = decl.var();
433 SkASSERT(fVariableMap.find(&var) == fVariableMap.end());
434
435 // For most variables, fVariableMap stores an index into fSlots, but for children,
436 // fVariableMap stores the index to pass to fSample(Shader|ColorFilter|Blender)
437 if (var.type().isEffectChild()) {
438 fVariableMap[&var] = fpCount++;
439 continue;
440 }
441
442 // Opaque types include child processors and GL objects (samplers, textures, etc).
443 // Of those, only child processors are legal variables.
444 SkASSERT(!var.type().isVoid());
445 SkASSERT(!var.type().isOpaque());
446
447 // getSlot() allocates space for the variable's value in fSlots, initializes it to zero,
448 // and populates fVariableMap.
449 size_t slot = this->getSlot(var),
450 nslots = var.type().slotCount();
451
452 // builtin variables are system-defined, with special semantics. The only builtin
453 // variable exposed to runtime effects is sk_FragCoord.
454 if (int builtin = var.modifiers().fLayout.fBuiltin; builtin >= 0) {
455 switch (builtin) {
456 case SK_FRAGCOORD_BUILTIN:
457 SkASSERT(nslots == 4);
458 this->writeToSlot(slot + 0, device.x.id);
459 this->writeToSlot(slot + 1, device.y.id);
460 this->writeToSlot(slot + 2, fBuilder->splat(0.0f).id);
461 this->writeToSlot(slot + 3, fBuilder->splat(1.0f).id);
462 break;
463 default:
464 SkDEBUGFAILF("Unsupported builtin %d", builtin);
465 }
466 continue;
467 }
468
469 // For uniforms, copy the supplied IDs over
470 if (is_uniform(var)) {
471 SkASSERT(uniformIter + nslots <= uniforms.end());
472 for (size_t i = 0; i < nslots; ++i) {
473 this->writeToSlot(slot + i, uniformIter[i]);
474 }
475 uniformIter += nslots;
476 continue;
477 }
478
479 // For other globals, populate with the initializer expression (if there is one)
480 if (decl.value()) {
481 Value val = this->writeExpression(*decl.value());
482 for (size_t i = 0; i < nslots; ++i) {
483 this->writeToSlot(slot + i, val[i]);
484 }
485 }
486 }
487 }
488 SkASSERT(uniformIter == uniforms.end());
489 }
490
getSlotValue(size_t slot,size_t nslots)491 Value SkVMGenerator::getSlotValue(size_t slot, size_t nslots) {
492 Value val(nslots);
493 for (size_t i = 0; i < nslots; ++i) {
494 val[i] = fSlots[slot + i].val;
495 }
496 return val;
497 }
498
getDebugFunctionInfo(const FunctionDeclaration & decl)499 int SkVMGenerator::getDebugFunctionInfo(const FunctionDeclaration& decl) {
500 SkASSERT(fDebugTrace);
501
502 std::string name = decl.description();
503
504 // Look for a matching SkVMFunctionInfo slot.
505 for (size_t index = 0; index < fDebugTrace->fFuncInfo.size(); ++index) {
506 if (fDebugTrace->fFuncInfo[index].name == name) {
507 return index;
508 }
509 }
510
511 // We've never called this function before; create a new slot to hold its information.
512 int slot = (int)fDebugTrace->fFuncInfo.size();
513 fDebugTrace->fFuncInfo.push_back(SkVMFunctionInfo{std::move(name)});
514 return slot;
515 }
516
writeFunction(const FunctionDefinition & function,SkSpan<skvm::Val> arguments)517 size_t SkVMGenerator::writeFunction(const FunctionDefinition& function,
518 SkSpan<skvm::Val> arguments) {
519 const FunctionDeclaration& decl = function.declaration();
520
521 int funcIndex = -1;
522 if (fDebugTrace) {
523 funcIndex = this->getDebugFunctionInfo(decl);
524 fBuilder->trace_enter(fTraceHookID, this->mask(), fTraceMask, funcIndex);
525 }
526
527 size_t returnSlot = this->getSlot(function);
528 fFunctionStack.push_back({/*fReturnSlot=*/returnSlot, /*fReturned=*/fBuilder->splat(0)});
529
530 // For all parameters, copy incoming argument IDs to our vector of (all) variable IDs
531 size_t argIdx = 0;
532 for (const Variable* p : decl.parameters()) {
533 size_t paramSlot = this->getSlot(*p),
534 nslots = p->type().slotCount();
535
536 for (size_t i = 0; i < nslots; ++i) {
537 fSlots[paramSlot + i].writtenTo = false;
538 this->writeToSlot(paramSlot + i, arguments[argIdx + i]);
539 }
540 argIdx += nslots;
541 }
542 SkASSERT(argIdx == arguments.size());
543
544 this->writeStatement(*function.body());
545
546 // Copy 'out' and 'inout' parameters back to their caller-supplied argument storage
547 argIdx = 0;
548 for (const Variable* p : decl.parameters()) {
549 size_t nslots = p->type().slotCount();
550
551 if (p->modifiers().fFlags & Modifiers::kOut_Flag) {
552 size_t paramSlot = this->getSlot(*p);
553 for (size_t i = 0; i < nslots; ++i) {
554 arguments[argIdx + i] = fSlots[paramSlot + i].val;
555 }
556 }
557 argIdx += nslots;
558 }
559 SkASSERT(argIdx == arguments.size());
560
561 fFunctionStack.pop_back();
562
563 if (fDebugTrace) {
564 fBuilder->trace_exit(fTraceHookID, this->mask(), fTraceMask, funcIndex);
565 }
566
567 return returnSlot;
568 }
569
writeToSlot(int slot,skvm::Val value)570 void SkVMGenerator::writeToSlot(int slot, skvm::Val value) {
571 if (fDebugTrace && (!fSlots[slot].writtenTo || fSlots[slot].val != value)) {
572 if (fProgram.fConfig->fSettings.fAllowTraceVarInSkVMDebugTrace) {
573 fBuilder->trace_var(fTraceHookID, this->mask(), fTraceMask, slot, i32(value));
574 }
575 fSlots[slot].writtenTo = true;
576 }
577
578 fSlots[slot].val = value;
579 }
580
addDebugSlotInfoForGroup(const std::string & varName,const Type & type,int line,int * groupIndex,int fnReturnValue)581 void SkVMGenerator::addDebugSlotInfoForGroup(const std::string& varName, const Type& type, int line,
582 int* groupIndex, int fnReturnValue) {
583 SkASSERT(fDebugTrace);
584 switch (type.typeKind()) {
585 case Type::TypeKind::kArray: {
586 int nslots = type.columns();
587 const Type& elemType = type.componentType();
588 for (int slot = 0; slot < nslots; ++slot) {
589 this->addDebugSlotInfoForGroup(varName + "[" + std::to_string(slot) + "]", elemType,
590 line, groupIndex, fnReturnValue);
591 }
592 break;
593 }
594 case Type::TypeKind::kStruct: {
595 for (const Type::Field& field : type.fields()) {
596 this->addDebugSlotInfoForGroup(varName + "." + std::string(field.fName),
597 *field.fType, line, groupIndex, fnReturnValue);
598 }
599 break;
600 }
601 default:
602 SkASSERTF(0, "unsupported slot type %d", (int)type.typeKind());
603 [[fallthrough]];
604
605 case Type::TypeKind::kScalar:
606 case Type::TypeKind::kVector:
607 case Type::TypeKind::kMatrix: {
608 Type::NumberKind numberKind = type.componentType().numberKind();
609 int nslots = type.slotCount();
610
611 for (int slot = 0; slot < nslots; ++slot) {
612 SkVMSlotInfo slotInfo;
613 slotInfo.name = varName;
614 slotInfo.columns = type.columns();
615 slotInfo.rows = type.rows();
616 slotInfo.componentIndex = slot;
617 slotInfo.groupIndex = (*groupIndex)++;
618 slotInfo.numberKind = numberKind;
619 slotInfo.line = line;
620 slotInfo.fnReturnValue = fnReturnValue;
621 fDebugTrace->fSlotInfo.push_back(std::move(slotInfo));
622 }
623 break;
624 }
625 }
626 }
627
addDebugSlotInfo(const std::string & varName,const Type & type,int line,int fnReturnValue)628 void SkVMGenerator::addDebugSlotInfo(const std::string& varName, const Type& type, int line,
629 int fnReturnValue) {
630 int groupIndex = 0;
631 this->addDebugSlotInfoForGroup(varName, type, line, &groupIndex, fnReturnValue);
632 SkASSERT((size_t)groupIndex == type.slotCount());
633 }
634
createSlot(const std::string & name,const Type & type,int line,int fnReturnValue)635 size_t SkVMGenerator::createSlot(const std::string& name,
636 const Type& type,
637 int line,
638 int fnReturnValue) {
639 size_t slot = fSlots.size(),
640 nslots = type.slotCount();
641
642 if (nslots > 0) {
643 if (fDebugTrace) {
644 // Our debug slot-info table should have the same length as the actual slot table.
645 SkASSERT(fDebugTrace->fSlotInfo.size() == slot);
646
647 // Append slot names and types to our debug slot-info table.
648 fDebugTrace->fSlotInfo.reserve(slot + nslots);
649 this->addDebugSlotInfo(name, type, line, fnReturnValue);
650
651 // Confirm that we added the expected number of slots.
652 SkASSERT(fDebugTrace->fSlotInfo.size() == (slot + nslots));
653 }
654
655 // Create brand new slots initialized to zero.
656 skvm::Val initialValue = fBuilder->splat(0.0f).id;
657 fSlots.insert(fSlots.end(), nslots, Slot{initialValue});
658 }
659 return slot;
660 }
661
getSlot(const Variable & v)662 size_t SkVMGenerator::getSlot(const Variable& v) {
663 auto entry = fVariableMap.find(&v);
664 if (entry != fVariableMap.end()) {
665 return entry->second;
666 }
667
668 size_t slot = this->createSlot(std::string(v.name()), v.type(), v.fLine, /*fnReturnValue=*/-1);
669 fVariableMap[&v] = slot;
670 return slot;
671 }
672
getSlot(const FunctionDefinition & fn)673 size_t SkVMGenerator::getSlot(const FunctionDefinition& fn) {
674 auto entry = fReturnValueMap.find(&fn);
675 if (entry != fReturnValueMap.end()) {
676 return entry->second;
677 }
678
679 const FunctionDeclaration& decl = fn.declaration();
680 int fnReturnValue = fDebugTrace ? this->getDebugFunctionInfo(decl) : -1;
681
682 size_t slot = this->createSlot("[" + std::string(decl.name()) + "].result",
683 decl.returnType(),
684 fn.fLine,
685 fnReturnValue);
686 fReturnValueMap[&fn] = slot;
687 return slot;
688 }
689
recursiveBinaryCompare(const Value & lVal,const Type & lType,const Value & rVal,const Type & rType,size_t * slotOffset,Value * result,const std::function<Value (skvm::F32 x,skvm::F32 y)> & float_comp,const std::function<Value (skvm::I32 x,skvm::I32 y)> & int_comp)690 void SkVMGenerator::recursiveBinaryCompare(
691 const Value& lVal,
692 const Type& lType,
693 const Value& rVal,
694 const Type& rType,
695 size_t* slotOffset,
696 Value* result,
697 const std::function<Value(skvm::F32 x, skvm::F32 y)>& float_comp,
698 const std::function<Value(skvm::I32 x, skvm::I32 y)>& int_comp) {
699 switch (lType.typeKind()) {
700 case Type::TypeKind::kStruct:
701 SkASSERT(rType.typeKind() == Type::TypeKind::kStruct);
702 // Go through all the fields
703 for (size_t f = 0; f < lType.fields().size(); ++f) {
704 const Type::Field& lField = lType.fields()[f];
705 const Type::Field& rField = rType.fields()[f];
706 this->recursiveBinaryCompare(lVal,
707 *lField.fType,
708 rVal,
709 *rField.fType,
710 slotOffset,
711 result,
712 float_comp,
713 int_comp);
714 }
715 break;
716
717 case Type::TypeKind::kArray:
718 case Type::TypeKind::kVector:
719 case Type::TypeKind::kMatrix:
720 SkASSERT(lType.typeKind() == rType.typeKind());
721 // Go through all the elements
722 for (int c = 0; c < lType.columns(); ++c) {
723 this->recursiveBinaryCompare(lVal,
724 lType.componentType(),
725 rVal,
726 rType.componentType(),
727 slotOffset,
728 result,
729 float_comp,
730 int_comp);
731 }
732 break;
733 default:
734 SkASSERT(lType.typeKind() == rType.typeKind() &&
735 lType.slotCount() == rType.slotCount());
736 Type::NumberKind nk = base_number_kind(lType);
737 auto L = lVal[*slotOffset];
738 auto R = rVal[*slotOffset];
739 (*result)[*slotOffset] =
740 i32(nk == Type::NumberKind::kFloat
741 ? float_comp(f32(L), f32(R))
742 : int_comp(i32(L), i32(R))).id;
743 *slotOffset += lType.slotCount();
744 break;
745 }
746 }
747
writeBinaryExpression(const BinaryExpression & b)748 Value SkVMGenerator::writeBinaryExpression(const BinaryExpression& b) {
749 const Expression& left = *b.left();
750 const Expression& right = *b.right();
751 Operator op = b.getOperator();
752 if (op.kind() == Token::Kind::TK_EQ) {
753 return this->writeStore(left, this->writeExpression(right));
754 }
755
756 const Type& lType = left.type();
757 const Type& rType = right.type();
758 bool lVecOrMtx = (lType.isVector() || lType.isMatrix());
759 bool rVecOrMtx = (rType.isVector() || rType.isMatrix());
760 bool isAssignment = op.isAssignment();
761 if (isAssignment) {
762 op = op.removeAssignment();
763 }
764 Type::NumberKind nk = base_number_kind(lType);
765
766 // A few ops require special treatment:
767 switch (op.kind()) {
768 case Token::Kind::TK_LOGICALAND: {
769 SkASSERT(!isAssignment);
770 SkASSERT(nk == Type::NumberKind::kBoolean);
771 skvm::I32 lVal = i32(this->writeExpression(left));
772 ScopedCondition shortCircuit(this, lVal);
773 skvm::I32 rVal = i32(this->writeExpression(right));
774 return lVal & rVal;
775 }
776 case Token::Kind::TK_LOGICALOR: {
777 SkASSERT(!isAssignment);
778 SkASSERT(nk == Type::NumberKind::kBoolean);
779 skvm::I32 lVal = i32(this->writeExpression(left));
780 ScopedCondition shortCircuit(this, ~lVal);
781 skvm::I32 rVal = i32(this->writeExpression(right));
782 return lVal | rVal;
783 }
784 case Token::Kind::TK_COMMA:
785 // We write the left side of the expression to preserve its side effects, even though we
786 // immediately discard the result.
787 this->writeExpression(left);
788 return this->writeExpression(right);
789 default:
790 break;
791 }
792
793 // All of the other ops always evaluate both sides of the expression
794 Value lVal = this->writeExpression(left),
795 rVal = this->writeExpression(right);
796
797 // Special case for M*V, V*M, M*M (but not V*V!)
798 if (op.kind() == Token::Kind::TK_STAR
799 && lVecOrMtx && rVecOrMtx && !(lType.isVector() && rType.isVector())) {
800 int rCols = rType.columns(),
801 rRows = rType.rows(),
802 lCols = lType.columns(),
803 lRows = lType.rows();
804 // M*V treats the vector as a column
805 if (rType.isVector()) {
806 std::swap(rCols, rRows);
807 }
808 SkASSERT(lCols == rRows);
809 SkASSERT(b.type().slotCount() == static_cast<size_t>(lRows * rCols));
810 Value result(lRows * rCols);
811 size_t resultIdx = 0;
812 const skvm::F32 zero = fBuilder->splat(0.0f);
813 for (int c = 0; c < rCols; ++c)
814 for (int r = 0; r < lRows; ++r) {
815 skvm::F32 sum = zero;
816 for (int j = 0; j < lCols; ++j) {
817 sum += f32(lVal[j*lRows + r]) * f32(rVal[c*rRows + j]);
818 }
819 result[resultIdx++] = sum;
820 }
821 SkASSERT(resultIdx == result.slots());
822 return isAssignment ? this->writeStore(left, result) : result;
823 }
824
825 size_t nslots = std::max(lVal.slots(), rVal.slots());
826
827 auto binary = [&](const std::function <Value(skvm::F32 x, skvm::F32 y)>& f_fn,
828 const std::function <Value(skvm::I32 x, skvm::I32 y)>& i_fn,
829 bool foldResults = false) -> Value {
830
831 Value result(nslots);
832 if (op.isEquality() && (lType.typeKind() == Type::TypeKind::kStruct ||
833 lType.typeKind() == Type::TypeKind::kArray)) {
834 // Shifting over lVal and rVal
835 size_t slotOffset = 0;
836 this->recursiveBinaryCompare(
837 lVal, lType, rVal, rType, &slotOffset, &result, f_fn, i_fn);
838 SkASSERT(slotOffset == nslots);
839 } else {
840 for (size_t slot = 0; slot < nslots; ++slot) {
841 // If one side is scalar, replicate it to all channels
842 skvm::Val L = lVal.slots() == 1 ? lVal[0] : lVal[slot],
843 R = rVal.slots() == 1 ? rVal[0] : rVal[slot];
844
845 if (nk == Type::NumberKind::kFloat) {
846 result[slot] = i32(f_fn(f32(L), f32(R)));
847 } else {
848 result[slot] = i32(i_fn(i32(L), i32(R)));
849 }
850 }
851 }
852
853 if (foldResults) {
854 // Just to be more explicit here we ask for a parameter and not detect it ourselves
855 SkASSERT(op.isEquality());
856 skvm::I32 folded = i32(result[0]);
857 for (size_t i = 1; i < nslots; ++i) {
858 if (op.kind() == Token::Kind::TK_NEQ) {
859 folded |= i32(result[i]);
860 } else {
861 folded &= i32(result[i]);
862 }
863 }
864 return folded;
865 }
866
867 return isAssignment ? this->writeStore(left, result) : result;
868 };
869
870 auto unsupported_f = [&](skvm::F32, skvm::F32) {
871 SkDEBUGFAIL("Unsupported operator");
872 return skvm::F32{};
873 };
874
875 switch (op.kind()) {
876 case Token::Kind::TK_EQEQ:
877 SkASSERT(!isAssignment);
878 return binary([](skvm::F32 x, skvm::F32 y) { return x == y; },
879 [](skvm::I32 x, skvm::I32 y) { return x == y; }, /*foldResults=*/ true);
880 case Token::Kind::TK_NEQ:
881 SkASSERT(!isAssignment);
882 return binary([](skvm::F32 x, skvm::F32 y) { return x != y; },
883 [](skvm::I32 x, skvm::I32 y) { return x != y; }, /*foldResults=*/ true);
884 case Token::Kind::TK_GT:
885 return binary([](skvm::F32 x, skvm::F32 y) { return x > y; },
886 [](skvm::I32 x, skvm::I32 y) { return x > y; });
887 case Token::Kind::TK_GTEQ:
888 return binary([](skvm::F32 x, skvm::F32 y) { return x >= y; },
889 [](skvm::I32 x, skvm::I32 y) { return x >= y; });
890 case Token::Kind::TK_LT:
891 return binary([](skvm::F32 x, skvm::F32 y) { return x < y; },
892 [](skvm::I32 x, skvm::I32 y) { return x < y; });
893 case Token::Kind::TK_LTEQ:
894 return binary([](skvm::F32 x, skvm::F32 y) { return x <= y; },
895 [](skvm::I32 x, skvm::I32 y) { return x <= y; });
896
897 case Token::Kind::TK_PLUS:
898 return binary([](skvm::F32 x, skvm::F32 y) { return x + y; },
899 [](skvm::I32 x, skvm::I32 y) { return x + y; });
900 case Token::Kind::TK_MINUS:
901 return binary([](skvm::F32 x, skvm::F32 y) { return x - y; },
902 [](skvm::I32 x, skvm::I32 y) { return x - y; });
903 case Token::Kind::TK_STAR:
904 return binary([](skvm::F32 x, skvm::F32 y) { return x ** y; },
905 [](skvm::I32 x, skvm::I32 y) { return x * y; });
906 case Token::Kind::TK_SLASH:
907 // Minimum spec (GLSL ES 1.0) has very loose requirements for integer operations.
908 // (Low-end GPUs may not have integer ALUs). Given that, we are allowed to do floating
909 // point division plus rounding. Section 10.28 of the spec even clarifies that the
910 // rounding mode is undefined (but round-towards-zero is the obvious/common choice).
911 return binary([](skvm::F32 x, skvm::F32 y) { return x / y; },
912 [](skvm::I32 x, skvm::I32 y) {
913 return skvm::trunc(skvm::to_F32(x) / skvm::to_F32(y));
914 });
915
916 case Token::Kind::TK_BITWISEXOR:
917 case Token::Kind::TK_LOGICALXOR:
918 return binary(unsupported_f, [](skvm::I32 x, skvm::I32 y) { return x ^ y; });
919 case Token::Kind::TK_BITWISEAND:
920 return binary(unsupported_f, [](skvm::I32 x, skvm::I32 y) { return x & y; });
921 case Token::Kind::TK_BITWISEOR:
922 return binary(unsupported_f, [](skvm::I32 x, skvm::I32 y) { return x | y; });
923
924 // These three operators are all 'reserved' (illegal) in our minimum spec, but will require
925 // implementation in the future.
926 case Token::Kind::TK_PERCENT:
927 case Token::Kind::TK_SHL:
928 case Token::Kind::TK_SHR:
929 default:
930 SkDEBUGFAIL("Unsupported operator");
931 return {};
932 }
933 }
934
writeAggregationConstructor(const AnyConstructor & c)935 Value SkVMGenerator::writeAggregationConstructor(const AnyConstructor& c) {
936 Value result(c.type().slotCount());
937 size_t resultIdx = 0;
938 for (const auto &arg : c.argumentSpan()) {
939 Value tmp = this->writeExpression(*arg);
940 for (size_t tmpSlot = 0; tmpSlot < tmp.slots(); ++tmpSlot) {
941 result[resultIdx++] = tmp[tmpSlot];
942 }
943 }
944 return result;
945 }
946
writeTypeConversion(const Value & src,Type::NumberKind srcKind,Type::NumberKind dstKind)947 Value SkVMGenerator::writeTypeConversion(const Value& src,
948 Type::NumberKind srcKind,
949 Type::NumberKind dstKind) {
950 // Conversion among "similar" types (floatN <-> halfN), (shortN <-> intN), etc. is a no-op.
951 if (srcKind == dstKind) {
952 return src;
953 }
954
955 // TODO: Handle signed vs. unsigned. GLSL ES 1.0 only has 'int', so no problem yet.
956 Value dst(src.slots());
957 switch (dstKind) {
958 case Type::NumberKind::kFloat:
959 if (srcKind == Type::NumberKind::kSigned) {
960 // int -> float
961 for (size_t i = 0; i < src.slots(); ++i) {
962 dst[i] = skvm::to_F32(i32(src[i]));
963 }
964 return dst;
965 }
966 if (srcKind == Type::NumberKind::kBoolean) {
967 // bool -> float
968 for (size_t i = 0; i < src.slots(); ++i) {
969 dst[i] = skvm::select(i32(src[i]), 1.0f, 0.0f);
970 }
971 return dst;
972 }
973 break;
974
975 case Type::NumberKind::kSigned:
976 if (srcKind == Type::NumberKind::kFloat) {
977 // float -> int
978 for (size_t i = 0; i < src.slots(); ++i) {
979 dst[i] = skvm::trunc(f32(src[i]));
980 }
981 return dst;
982 }
983 if (srcKind == Type::NumberKind::kBoolean) {
984 // bool -> int
985 for (size_t i = 0; i < src.slots(); ++i) {
986 dst[i] = skvm::select(i32(src[i]), 1, 0);
987 }
988 return dst;
989 }
990 break;
991
992 case Type::NumberKind::kBoolean:
993 if (srcKind == Type::NumberKind::kSigned) {
994 // int -> bool
995 for (size_t i = 0; i < src.slots(); ++i) {
996 dst[i] = i32(src[i]) != 0;
997 }
998 return dst;
999 }
1000 if (srcKind == Type::NumberKind::kFloat) {
1001 // float -> bool
1002 for (size_t i = 0; i < src.slots(); ++i) {
1003 dst[i] = f32(src[i]) != 0.0;
1004 }
1005 return dst;
1006 }
1007 break;
1008
1009 default:
1010 break;
1011 }
1012 SkDEBUGFAILF("Unsupported type conversion: %d -> %d", (int)srcKind, (int)dstKind);
1013 return {};
1014 }
1015
writeConstructorCast(const AnyConstructor & c)1016 Value SkVMGenerator::writeConstructorCast(const AnyConstructor& c) {
1017 auto arguments = c.argumentSpan();
1018 SkASSERT(arguments.size() == 1);
1019 const Expression& argument = *arguments.front();
1020
1021 const Type& srcType = argument.type();
1022 const Type& dstType = c.type();
1023 Type::NumberKind srcKind = base_number_kind(srcType);
1024 Type::NumberKind dstKind = base_number_kind(dstType);
1025 Value src = this->writeExpression(argument);
1026 return this->writeTypeConversion(src, srcKind, dstKind);
1027 }
1028
writeConstructorSplat(const ConstructorSplat & c)1029 Value SkVMGenerator::writeConstructorSplat(const ConstructorSplat& c) {
1030 SkASSERT(c.type().isVector());
1031 SkASSERT(c.argument()->type().isScalar());
1032 int columns = c.type().columns();
1033
1034 // Splat the argument across all components of a vector.
1035 Value src = this->writeExpression(*c.argument());
1036 Value dst(columns);
1037 for (int i = 0; i < columns; ++i) {
1038 dst[i] = src[0];
1039 }
1040 return dst;
1041 }
1042
writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix & ctor)1043 Value SkVMGenerator::writeConstructorDiagonalMatrix(const ConstructorDiagonalMatrix& ctor) {
1044 const Type& dstType = ctor.type();
1045 SkASSERT(dstType.isMatrix());
1046 SkASSERT(ctor.argument()->type().matches(dstType.componentType()));
1047
1048 Value src = this->writeExpression(*ctor.argument());
1049 Value dst(dstType.rows() * dstType.columns());
1050 size_t dstIndex = 0;
1051
1052 // Matrix-from-scalar builds a diagonal scale matrix
1053 const skvm::F32 zero = fBuilder->splat(0.0f);
1054 for (int c = 0; c < dstType.columns(); ++c) {
1055 for (int r = 0; r < dstType.rows(); ++r) {
1056 dst[dstIndex++] = (c == r ? f32(src) : zero);
1057 }
1058 }
1059
1060 SkASSERT(dstIndex == dst.slots());
1061 return dst;
1062 }
1063
writeConstructorMatrixResize(const ConstructorMatrixResize & ctor)1064 Value SkVMGenerator::writeConstructorMatrixResize(const ConstructorMatrixResize& ctor) {
1065 const Type& srcType = ctor.argument()->type();
1066 const Type& dstType = ctor.type();
1067 Value src = this->writeExpression(*ctor.argument());
1068 Value dst(dstType.rows() * dstType.columns());
1069
1070 // Matrix-from-matrix uses src where it overlaps, and fills in missing fields with identity.
1071 size_t dstIndex = 0;
1072 for (int c = 0; c < dstType.columns(); ++c) {
1073 for (int r = 0; r < dstType.rows(); ++r) {
1074 if (c < srcType.columns() && r < srcType.rows()) {
1075 dst[dstIndex++] = src[c * srcType.rows() + r];
1076 } else {
1077 dst[dstIndex++] = fBuilder->splat(c == r ? 1.0f : 0.0f);
1078 }
1079 }
1080 }
1081
1082 SkASSERT(dstIndex == dst.slots());
1083 return dst;
1084 }
1085
fieldSlotOffset(const FieldAccess & expr)1086 size_t SkVMGenerator::fieldSlotOffset(const FieldAccess& expr) {
1087 size_t offset = 0;
1088 for (int i = 0; i < expr.fieldIndex(); ++i) {
1089 offset += (*expr.base()->type().fields()[i].fType).slotCount();
1090 }
1091 return offset;
1092 }
1093
writeFieldAccess(const FieldAccess & expr)1094 Value SkVMGenerator::writeFieldAccess(const FieldAccess& expr) {
1095 Value base = this->writeExpression(*expr.base());
1096 Value field(expr.type().slotCount());
1097 size_t offset = this->fieldSlotOffset(expr);
1098 for (size_t i = 0; i < field.slots(); ++i) {
1099 field[i] = base[offset + i];
1100 }
1101 return field;
1102 }
1103
indexSlotOffset(const IndexExpression & expr)1104 size_t SkVMGenerator::indexSlotOffset(const IndexExpression& expr) {
1105 Value index = this->writeExpression(*expr.index());
1106 int indexValue = -1;
1107 SkAssertResult(fBuilder->allImm(index[0], &indexValue));
1108
1109 // When indexing by a literal, the front-end guarantees that we don't go out of bounds.
1110 // But when indexing by a loop variable, it's possible to generate out-of-bounds access.
1111 // The GLSL spec leaves that behavior undefined - we'll just clamp everything here.
1112 indexValue = SkTPin(indexValue, 0, expr.base()->type().columns() - 1);
1113
1114 size_t stride = expr.type().slotCount();
1115 return indexValue * stride;
1116 }
1117
writeIndexExpression(const IndexExpression & expr)1118 Value SkVMGenerator::writeIndexExpression(const IndexExpression& expr) {
1119 Value base = this->writeExpression(*expr.base());
1120 Value element(expr.type().slotCount());
1121 size_t offset = this->indexSlotOffset(expr);
1122 for (size_t i = 0; i < element.slots(); ++i) {
1123 element[i] = base[offset + i];
1124 }
1125 return element;
1126 }
1127
writeVariableExpression(const VariableReference & expr)1128 Value SkVMGenerator::writeVariableExpression(const VariableReference& expr) {
1129 size_t slot = this->getSlot(*expr.variable());
1130 return this->getSlotValue(slot, expr.type().slotCount());
1131 }
1132
writeMatrixInverse2x2(const Value & m)1133 Value SkVMGenerator::writeMatrixInverse2x2(const Value& m) {
1134 SkASSERT(m.slots() == 4);
1135 skvm::F32 a = f32(m[0]),
1136 b = f32(m[1]),
1137 c = f32(m[2]),
1138 d = f32(m[3]);
1139 skvm::F32 idet = 1.0f / (a*d - b*c);
1140
1141 Value result(m.slots());
1142 result[0] = ( d ** idet);
1143 result[1] = (-b ** idet);
1144 result[2] = (-c ** idet);
1145 result[3] = ( a ** idet);
1146 return result;
1147 }
1148
writeMatrixInverse3x3(const Value & m)1149 Value SkVMGenerator::writeMatrixInverse3x3(const Value& m) {
1150 SkASSERT(m.slots() == 9);
1151 skvm::F32 a11 = f32(m[0]), a12 = f32(m[3]), a13 = f32(m[6]),
1152 a21 = f32(m[1]), a22 = f32(m[4]), a23 = f32(m[7]),
1153 a31 = f32(m[2]), a32 = f32(m[5]), a33 = f32(m[8]);
1154 skvm::F32 idet = 1.0f / (a11*a22*a33 + a12*a23*a31 + a13*a21*a32 -
1155 a11*a23*a32 - a12*a21*a33 - a13*a22*a31);
1156
1157 Value result(m.slots());
1158 result[0] = ((a22**a33 - a23**a32) ** idet);
1159 result[1] = ((a23**a31 - a21**a33) ** idet);
1160 result[2] = ((a21**a32 - a22**a31) ** idet);
1161 result[3] = ((a13**a32 - a12**a33) ** idet);
1162 result[4] = ((a11**a33 - a13**a31) ** idet);
1163 result[5] = ((a12**a31 - a11**a32) ** idet);
1164 result[6] = ((a12**a23 - a13**a22) ** idet);
1165 result[7] = ((a13**a21 - a11**a23) ** idet);
1166 result[8] = ((a11**a22 - a12**a21) ** idet);
1167 return result;
1168 }
1169
writeMatrixInverse4x4(const Value & m)1170 Value SkVMGenerator::writeMatrixInverse4x4(const Value& m) {
1171 SkASSERT(m.slots() == 16);
1172 skvm::F32 a00 = f32(m[0]), a10 = f32(m[4]), a20 = f32(m[ 8]), a30 = f32(m[12]),
1173 a01 = f32(m[1]), a11 = f32(m[5]), a21 = f32(m[ 9]), a31 = f32(m[13]),
1174 a02 = f32(m[2]), a12 = f32(m[6]), a22 = f32(m[10]), a32 = f32(m[14]),
1175 a03 = f32(m[3]), a13 = f32(m[7]), a23 = f32(m[11]), a33 = f32(m[15]);
1176
1177 skvm::F32 b00 = a00**a11 - a01**a10,
1178 b01 = a00**a12 - a02**a10,
1179 b02 = a00**a13 - a03**a10,
1180 b03 = a01**a12 - a02**a11,
1181 b04 = a01**a13 - a03**a11,
1182 b05 = a02**a13 - a03**a12,
1183 b06 = a20**a31 - a21**a30,
1184 b07 = a20**a32 - a22**a30,
1185 b08 = a20**a33 - a23**a30,
1186 b09 = a21**a32 - a22**a31,
1187 b10 = a21**a33 - a23**a31,
1188 b11 = a22**a33 - a23**a32;
1189
1190 skvm::F32 idet = 1.0f / (b00**b11 - b01**b10 + b02**b09 + b03**b08 - b04**b07 + b05**b06);
1191
1192 b00 *= idet;
1193 b01 *= idet;
1194 b02 *= idet;
1195 b03 *= idet;
1196 b04 *= idet;
1197 b05 *= idet;
1198 b06 *= idet;
1199 b07 *= idet;
1200 b08 *= idet;
1201 b09 *= idet;
1202 b10 *= idet;
1203 b11 *= idet;
1204
1205 Value result(m.slots());
1206 result[ 0] = (a11*b11 - a12*b10 + a13*b09);
1207 result[ 1] = (a02*b10 - a01*b11 - a03*b09);
1208 result[ 2] = (a31*b05 - a32*b04 + a33*b03);
1209 result[ 3] = (a22*b04 - a21*b05 - a23*b03);
1210 result[ 4] = (a12*b08 - a10*b11 - a13*b07);
1211 result[ 5] = (a00*b11 - a02*b08 + a03*b07);
1212 result[ 6] = (a32*b02 - a30*b05 - a33*b01);
1213 result[ 7] = (a20*b05 - a22*b02 + a23*b01);
1214 result[ 8] = (a10*b10 - a11*b08 + a13*b06);
1215 result[ 9] = (a01*b08 - a00*b10 - a03*b06);
1216 result[10] = (a30*b04 - a31*b02 + a33*b00);
1217 result[11] = (a21*b02 - a20*b04 - a23*b00);
1218 result[12] = (a11*b07 - a10*b09 - a12*b06);
1219 result[13] = (a00*b09 - a01*b07 + a02*b06);
1220 result[14] = (a31*b01 - a30*b03 - a32*b00);
1221 result[15] = (a20*b03 - a21*b01 + a22*b00);
1222 return result;
1223 }
1224
writeChildCall(const ChildCall & c)1225 Value SkVMGenerator::writeChildCall(const ChildCall& c) {
1226 auto child_it = fVariableMap.find(&c.child());
1227 SkASSERT(child_it != fVariableMap.end());
1228
1229 const Expression* arg = c.arguments()[0].get();
1230 Value argVal = this->writeExpression(*arg);
1231 skvm::Color color;
1232
1233 switch (c.child().type().typeKind()) {
1234 case Type::TypeKind::kShader: {
1235 SkASSERT(c.arguments().size() == 1);
1236 SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fFloat2));
1237 skvm::Coord coord = {f32(argVal[0]), f32(argVal[1])};
1238 color = fCallbacks->sampleShader(child_it->second, coord);
1239 break;
1240 }
1241 case Type::TypeKind::kColorFilter: {
1242 SkASSERT(c.arguments().size() == 1);
1243 SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fHalf4) ||
1244 arg->type().matches(*fProgram.fContext->fTypes.fFloat4));
1245 skvm::Color inColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
1246 color = fCallbacks->sampleColorFilter(child_it->second, inColor);
1247 break;
1248 }
1249 case Type::TypeKind::kBlender: {
1250 SkASSERT(c.arguments().size() == 2);
1251 SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fHalf4) ||
1252 arg->type().matches(*fProgram.fContext->fTypes.fFloat4));
1253 skvm::Color srcColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
1254
1255 arg = c.arguments()[1].get();
1256 argVal = this->writeExpression(*arg);
1257 SkASSERT(arg->type().matches(*fProgram.fContext->fTypes.fHalf4) ||
1258 arg->type().matches(*fProgram.fContext->fTypes.fFloat4));
1259 skvm::Color dstColor = {f32(argVal[0]), f32(argVal[1]), f32(argVal[2]), f32(argVal[3])};
1260
1261 color = fCallbacks->sampleBlender(child_it->second, srcColor, dstColor);
1262 break;
1263 }
1264 default: {
1265 SkDEBUGFAILF("cannot sample from type '%s'", c.child().type().description().c_str());
1266 }
1267 }
1268
1269 Value result(4);
1270 result[0] = color.r;
1271 result[1] = color.g;
1272 result[2] = color.b;
1273 result[3] = color.a;
1274 return result;
1275 }
1276
writeIntrinsicCall(const FunctionCall & c)1277 Value SkVMGenerator::writeIntrinsicCall(const FunctionCall& c) {
1278 IntrinsicKind intrinsicKind = c.function().intrinsicKind();
1279 SkASSERT(intrinsicKind != kNotIntrinsic);
1280
1281 const size_t nargs = c.arguments().size();
1282 const size_t kMaxArgs = 3; // eg: clamp, mix, smoothstep
1283 Value args[kMaxArgs];
1284 SkASSERT(nargs >= 1 && nargs <= SK_ARRAY_COUNT(args));
1285
1286 // All other intrinsics have at most three args, and those can all be evaluated up front:
1287 for (size_t i = 0; i < nargs; ++i) {
1288 args[i] = this->writeExpression(*c.arguments()[i]);
1289 }
1290 Type::NumberKind nk = base_number_kind(c.arguments()[0]->type());
1291
1292 auto binary = [&](auto&& fn) {
1293 // Binary intrinsics are (vecN, vecN), (vecN, float), or (float, vecN)
1294 size_t nslots = std::max(args[0].slots(), args[1].slots());
1295 Value result(nslots);
1296 SkASSERT(args[0].slots() == nslots || args[0].slots() == 1);
1297 SkASSERT(args[1].slots() == nslots || args[1].slots() == 1);
1298
1299 for (size_t i = 0; i < nslots; ++i) {
1300 result[i] = fn({fBuilder, args[0][args[0].slots() == 1 ? 0 : i]},
1301 {fBuilder, args[1][args[1].slots() == 1 ? 0 : i]});
1302 }
1303 return result;
1304 };
1305
1306 auto ternary = [&](auto&& fn) {
1307 // Ternary intrinsics are some combination of vecN and float
1308 size_t nslots = std::max({args[0].slots(), args[1].slots(), args[2].slots()});
1309 Value result(nslots);
1310 SkASSERT(args[0].slots() == nslots || args[0].slots() == 1);
1311 SkASSERT(args[1].slots() == nslots || args[1].slots() == 1);
1312 SkASSERT(args[2].slots() == nslots || args[2].slots() == 1);
1313
1314 for (size_t i = 0; i < nslots; ++i) {
1315 result[i] = fn({fBuilder, args[0][args[0].slots() == 1 ? 0 : i]},
1316 {fBuilder, args[1][args[1].slots() == 1 ? 0 : i]},
1317 {fBuilder, args[2][args[2].slots() == 1 ? 0 : i]});
1318 }
1319 return result;
1320 };
1321
1322 auto dot = [&](const Value& x, const Value& y) {
1323 SkASSERT(x.slots() == y.slots());
1324 skvm::F32 result = f32(x[0]) * f32(y[0]);
1325 for (size_t i = 1; i < x.slots(); ++i) {
1326 result += f32(x[i]) * f32(y[i]);
1327 }
1328 return result;
1329 };
1330
1331 switch (intrinsicKind) {
1332 case k_radians_IntrinsicKind:
1333 return unary(args[0], [](skvm::F32 deg) { return deg * (SK_FloatPI / 180); });
1334 case k_degrees_IntrinsicKind:
1335 return unary(args[0], [](skvm::F32 rad) { return rad * (180 / SK_FloatPI); });
1336
1337 case k_sin_IntrinsicKind: return unary(args[0], skvm::approx_sin);
1338 case k_cos_IntrinsicKind: return unary(args[0], skvm::approx_cos);
1339 case k_tan_IntrinsicKind: return unary(args[0], skvm::approx_tan);
1340
1341 case k_asin_IntrinsicKind: return unary(args[0], skvm::approx_asin);
1342 case k_acos_IntrinsicKind: return unary(args[0], skvm::approx_acos);
1343
1344 case k_atan_IntrinsicKind: return nargs == 1 ? unary(args[0], skvm::approx_atan)
1345 : binary(skvm::approx_atan2);
1346
1347 case k_pow_IntrinsicKind:
1348 return binary([](skvm::F32 x, skvm::F32 y) { return skvm::approx_powf(x, y); });
1349 case k_exp_IntrinsicKind: return unary(args[0], skvm::approx_exp);
1350 case k_log_IntrinsicKind: return unary(args[0], skvm::approx_log);
1351 case k_exp2_IntrinsicKind: return unary(args[0], skvm::approx_pow2);
1352 case k_log2_IntrinsicKind: return unary(args[0], skvm::approx_log2);
1353
1354 case k_sqrt_IntrinsicKind: return unary(args[0], skvm::sqrt);
1355 case k_inversesqrt_IntrinsicKind:
1356 return unary(args[0], [](skvm::F32 x) { return 1.0f / skvm::sqrt(x); });
1357
1358 case k_abs_IntrinsicKind: return unary(args[0], skvm::abs);
1359 case k_sign_IntrinsicKind:
1360 return unary(args[0], [](skvm::F32 x) { return select(x < 0, -1.0f,
1361 select(x > 0, +1.0f, 0.0f)); });
1362 case k_floor_IntrinsicKind: return unary(args[0], skvm::floor);
1363 case k_ceil_IntrinsicKind: return unary(args[0], skvm::ceil);
1364 case k_fract_IntrinsicKind: return unary(args[0], skvm::fract);
1365 case k_mod_IntrinsicKind:
1366 return binary([](skvm::F32 x, skvm::F32 y) { return x - y*skvm::floor(x / y); });
1367
1368 case k_min_IntrinsicKind:
1369 return binary([](skvm::F32 x, skvm::F32 y) { return skvm::min(x, y); });
1370 case k_max_IntrinsicKind:
1371 return binary([](skvm::F32 x, skvm::F32 y) { return skvm::max(x, y); });
1372 case k_clamp_IntrinsicKind:
1373 return ternary(
1374 [](skvm::F32 x, skvm::F32 lo, skvm::F32 hi) { return skvm::clamp(x, lo, hi); });
1375 case k_saturate_IntrinsicKind:
1376 return unary(args[0], [](skvm::F32 x) { return skvm::clamp01(x); });
1377 case k_mix_IntrinsicKind:
1378 return ternary(
1379 [](skvm::F32 x, skvm::F32 y, skvm::F32 t) { return skvm::lerp(x, y, t); });
1380 case k_step_IntrinsicKind:
1381 return binary([](skvm::F32 edge, skvm::F32 x) { return select(x < edge, 0.0f, 1.0f); });
1382 case k_smoothstep_IntrinsicKind:
1383 return ternary([](skvm::F32 edge0, skvm::F32 edge1, skvm::F32 x) {
1384 skvm::F32 t = skvm::clamp01((x - edge0) / (edge1 - edge0));
1385 return t ** t ** (3 - 2 ** t);
1386 });
1387
1388 case k_length_IntrinsicKind: return skvm::sqrt(dot(args[0], args[0]));
1389 case k_distance_IntrinsicKind: {
1390 Value vec = binary([](skvm::F32 x, skvm::F32 y) { return x - y; });
1391 return skvm::sqrt(dot(vec, vec));
1392 }
1393 case k_dot_IntrinsicKind: return dot(args[0], args[1]);
1394 case k_cross_IntrinsicKind: {
1395 skvm::F32 ax = f32(args[0][0]), ay = f32(args[0][1]), az = f32(args[0][2]),
1396 bx = f32(args[1][0]), by = f32(args[1][1]), bz = f32(args[1][2]);
1397 Value result(3);
1398 result[0] = ay**bz - az**by;
1399 result[1] = az**bx - ax**bz;
1400 result[2] = ax**by - ay**bx;
1401 return result;
1402 }
1403 case k_normalize_IntrinsicKind: {
1404 skvm::F32 invLen = 1.0f / skvm::sqrt(dot(args[0], args[0]));
1405 return unary(args[0], [&](skvm::F32 x) { return x ** invLen; });
1406 }
1407 case k_faceforward_IntrinsicKind: {
1408 const Value &N = args[0],
1409 &I = args[1],
1410 &Nref = args[2];
1411
1412 skvm::F32 dotNrefI = dot(Nref, I);
1413 return unary(N, [&](skvm::F32 n) { return select(dotNrefI<0, n, -n); });
1414 }
1415 case k_reflect_IntrinsicKind: {
1416 const Value &I = args[0],
1417 &N = args[1];
1418
1419 skvm::F32 dotNI = dot(N, I);
1420 return binary([&](skvm::F32 i, skvm::F32 n) {
1421 return i - 2**dotNI**n;
1422 });
1423 }
1424 case k_refract_IntrinsicKind: {
1425 const Value &I = args[0],
1426 &N = args[1];
1427 skvm::F32 eta = f32(args[2]);
1428
1429 skvm::F32 dotNI = dot(N, I),
1430 k = 1 - eta**eta**(1 - dotNI**dotNI);
1431 return binary([&](skvm::F32 i, skvm::F32 n) {
1432 return select(k<0, 0.0f, eta**i - (eta**dotNI + sqrt(k))**n);
1433 });
1434 }
1435
1436 case k_matrixCompMult_IntrinsicKind:
1437 return binary([](skvm::F32 x, skvm::F32 y) { return x ** y; });
1438 case k_inverse_IntrinsicKind: {
1439 switch (args[0].slots()) {
1440 case 4: return this->writeMatrixInverse2x2(args[0]);
1441 case 9: return this->writeMatrixInverse3x3(args[0]);
1442 case 16: return this->writeMatrixInverse4x4(args[0]);
1443 default:
1444 SkDEBUGFAIL("Invalid call to inverse");
1445 return {};
1446 }
1447 }
1448
1449 case k_lessThan_IntrinsicKind:
1450 return nk == Type::NumberKind::kFloat
1451 ? binary([](skvm::F32 x, skvm::F32 y) { return x < y; })
1452 : binary([](skvm::I32 x, skvm::I32 y) { return x < y; });
1453 case k_lessThanEqual_IntrinsicKind:
1454 return nk == Type::NumberKind::kFloat
1455 ? binary([](skvm::F32 x, skvm::F32 y) { return x <= y; })
1456 : binary([](skvm::I32 x, skvm::I32 y) { return x <= y; });
1457 case k_greaterThan_IntrinsicKind:
1458 return nk == Type::NumberKind::kFloat
1459 ? binary([](skvm::F32 x, skvm::F32 y) { return x > y; })
1460 : binary([](skvm::I32 x, skvm::I32 y) { return x > y; });
1461 case k_greaterThanEqual_IntrinsicKind:
1462 return nk == Type::NumberKind::kFloat
1463 ? binary([](skvm::F32 x, skvm::F32 y) { return x >= y; })
1464 : binary([](skvm::I32 x, skvm::I32 y) { return x >= y; });
1465
1466 case k_equal_IntrinsicKind:
1467 return nk == Type::NumberKind::kFloat
1468 ? binary([](skvm::F32 x, skvm::F32 y) { return x == y; })
1469 : binary([](skvm::I32 x, skvm::I32 y) { return x == y; });
1470 case k_notEqual_IntrinsicKind:
1471 return nk == Type::NumberKind::kFloat
1472 ? binary([](skvm::F32 x, skvm::F32 y) { return x != y; })
1473 : binary([](skvm::I32 x, skvm::I32 y) { return x != y; });
1474
1475 case k_any_IntrinsicKind: {
1476 skvm::I32 result = i32(args[0][0]);
1477 for (size_t i = 1; i < args[0].slots(); ++i) {
1478 result |= i32(args[0][i]);
1479 }
1480 return result;
1481 }
1482 case k_all_IntrinsicKind: {
1483 skvm::I32 result = i32(args[0][0]);
1484 for (size_t i = 1; i < args[0].slots(); ++i) {
1485 result &= i32(args[0][i]);
1486 }
1487 return result;
1488 }
1489 case k_not_IntrinsicKind: return unary(args[0], [](skvm::I32 x) { return ~x; });
1490
1491 case k_toLinearSrgb_IntrinsicKind: {
1492 skvm::Color color = {
1493 f32(args[0][0]), f32(args[0][1]), f32(args[0][2]), fBuilder->splat(1.0f)};
1494 color = fCallbacks->toLinearSrgb(color);
1495 Value result(3);
1496 result[0] = color.r;
1497 result[1] = color.g;
1498 result[2] = color.b;
1499 return result;
1500 }
1501 case k_fromLinearSrgb_IntrinsicKind: {
1502 skvm::Color color = {
1503 f32(args[0][0]), f32(args[0][1]), f32(args[0][2]), fBuilder->splat(1.0f)};
1504 color = fCallbacks->fromLinearSrgb(color);
1505 Value result(3);
1506 result[0] = color.r;
1507 result[1] = color.g;
1508 result[2] = color.b;
1509 return result;
1510 }
1511
1512 default:
1513 SkDEBUGFAILF("unsupported intrinsic %s", c.function().description().c_str());
1514 return {};
1515 }
1516 SkUNREACHABLE;
1517 }
1518
writeFunctionCall(const FunctionCall & f)1519 Value SkVMGenerator::writeFunctionCall(const FunctionCall& f) {
1520 if (f.function().isIntrinsic() && !f.function().definition()) {
1521 return this->writeIntrinsicCall(f);
1522 }
1523
1524 const FunctionDeclaration& decl = f.function();
1525 SkASSERTF(decl.definition(), "no definition for function '%s'", decl.description().c_str());
1526 const FunctionDefinition& funcDef = *decl.definition();
1527
1528 // Evaluate all arguments, gather the results into a contiguous list of IDs
1529 std::vector<skvm::Val> argVals;
1530 for (const auto& arg : f.arguments()) {
1531 Value v = this->writeExpression(*arg);
1532 for (size_t i = 0; i < v.slots(); ++i) {
1533 argVals.push_back(v[i]);
1534 }
1535 }
1536
1537 size_t returnSlot;
1538 {
1539 // This merges currentFunction().fReturned into fConditionMask. Lanes that conditionally
1540 // returned in the current function would otherwise resume execution within the child.
1541 ScopedCondition m(this, ~currentFunction().fReturned);
1542 returnSlot = this->writeFunction(funcDef, SkMakeSpan(argVals));
1543 }
1544
1545 // Propagate new values of any 'out' params back to the original arguments
1546 const std::unique_ptr<Expression>* argIter = f.arguments().begin();
1547 size_t valIdx = 0;
1548 for (const Variable* p : decl.parameters()) {
1549 size_t nslots = p->type().slotCount();
1550 if (p->modifiers().fFlags & Modifiers::kOut_Flag) {
1551 Value v(nslots);
1552 for (size_t i = 0; i < nslots; ++i) {
1553 v[i] = argVals[valIdx + i];
1554 }
1555 const std::unique_ptr<Expression>& arg = *argIter;
1556 this->writeStore(*arg, v);
1557 }
1558 valIdx += nslots;
1559 argIter++;
1560 }
1561
1562 // Create a result Value from the return slot
1563 return this->getSlotValue(returnSlot, f.type().slotCount());
1564 }
1565
writeExternalFunctionCall(const ExternalFunctionCall & c)1566 Value SkVMGenerator::writeExternalFunctionCall(const ExternalFunctionCall& c) {
1567 // Evaluate all arguments, gather the results into a contiguous list of F32
1568 std::vector<skvm::F32> args;
1569 for (const auto& arg : c.arguments()) {
1570 Value v = this->writeExpression(*arg);
1571 for (size_t i = 0; i < v.slots(); ++i) {
1572 args.push_back(f32(v[i]));
1573 }
1574 }
1575
1576 // Create storage for the return value
1577 size_t nslots = c.type().slotCount();
1578 std::vector<skvm::F32> result(nslots, fBuilder->splat(0.0f));
1579
1580 c.function().call(fBuilder, args.data(), result.data(), this->mask());
1581
1582 // Convert from 'vector of F32' to Value
1583 Value resultVal(nslots);
1584 for (size_t i = 0; i < nslots; ++i) {
1585 resultVal[i] = result[i];
1586 }
1587
1588 return resultVal;
1589 }
1590
writeLiteral(const Literal & l)1591 Value SkVMGenerator::writeLiteral(const Literal& l) {
1592 if (l.type().isFloat()) {
1593 return fBuilder->splat(l.as<Literal>().floatValue());
1594 }
1595 if (l.type().isInteger()) {
1596 return fBuilder->splat(static_cast<int>(l.as<Literal>().intValue()));
1597 }
1598 SkASSERT(l.type().isBoolean());
1599 return fBuilder->splat(l.as<Literal>().boolValue() ? ~0 : 0);
1600 }
1601
writePrefixExpression(const PrefixExpression & p)1602 Value SkVMGenerator::writePrefixExpression(const PrefixExpression& p) {
1603 Value val = this->writeExpression(*p.operand());
1604
1605 switch (p.getOperator().kind()) {
1606 case Token::Kind::TK_PLUSPLUS:
1607 case Token::Kind::TK_MINUSMINUS: {
1608 bool incr = p.getOperator().kind() == Token::Kind::TK_PLUSPLUS;
1609
1610 switch (base_number_kind(p.type())) {
1611 case Type::NumberKind::kFloat:
1612 val = f32(val) + fBuilder->splat(incr ? 1.0f : -1.0f);
1613 break;
1614 case Type::NumberKind::kSigned:
1615 val = i32(val) + fBuilder->splat(incr ? 1 : -1);
1616 break;
1617 default:
1618 SkASSERT(false);
1619 return {};
1620 }
1621 return this->writeStore(*p.operand(), val);
1622 }
1623 case Token::Kind::TK_MINUS: {
1624 switch (base_number_kind(p.type())) {
1625 case Type::NumberKind::kFloat:
1626 return this->unary(val, [](skvm::F32 x) { return -x; });
1627 case Type::NumberKind::kSigned:
1628 return this->unary(val, [](skvm::I32 x) { return -x; });
1629 default:
1630 SkASSERT(false);
1631 return {};
1632 }
1633 }
1634 case Token::Kind::TK_LOGICALNOT:
1635 case Token::Kind::TK_BITWISENOT:
1636 return this->unary(val, [](skvm::I32 x) { return ~x; });
1637 default:
1638 SkASSERT(false);
1639 return {};
1640 }
1641 }
1642
writePostfixExpression(const PostfixExpression & p)1643 Value SkVMGenerator::writePostfixExpression(const PostfixExpression& p) {
1644 switch (p.getOperator().kind()) {
1645 case Token::Kind::TK_PLUSPLUS:
1646 case Token::Kind::TK_MINUSMINUS: {
1647 Value old = this->writeExpression(*p.operand()),
1648 val = old;
1649 SkASSERT(val.slots() == 1);
1650 bool incr = p.getOperator().kind() == Token::Kind::TK_PLUSPLUS;
1651
1652 switch (base_number_kind(p.type())) {
1653 case Type::NumberKind::kFloat:
1654 val = f32(val) + fBuilder->splat(incr ? 1.0f : -1.0f);
1655 break;
1656 case Type::NumberKind::kSigned:
1657 val = i32(val) + fBuilder->splat(incr ? 1 : -1);
1658 break;
1659 default:
1660 SkASSERT(false);
1661 return {};
1662 }
1663 this->writeStore(*p.operand(), val);
1664 return old;
1665 }
1666 default:
1667 SkASSERT(false);
1668 return {};
1669 }
1670 }
1671
writeSwizzle(const Swizzle & s)1672 Value SkVMGenerator::writeSwizzle(const Swizzle& s) {
1673 Value base = this->writeExpression(*s.base());
1674 Value swizzled(s.components().size());
1675 for (size_t i = 0; i < s.components().size(); ++i) {
1676 swizzled[i] = base[s.components()[i]];
1677 }
1678 return swizzled;
1679 }
1680
writeTernaryExpression(const TernaryExpression & t)1681 Value SkVMGenerator::writeTernaryExpression(const TernaryExpression& t) {
1682 skvm::I32 test = i32(this->writeExpression(*t.test()));
1683 Value ifTrue, ifFalse;
1684
1685 {
1686 ScopedCondition m(this, test);
1687 ifTrue = this->writeExpression(*t.ifTrue());
1688 }
1689 {
1690 ScopedCondition m(this, ~test);
1691 ifFalse = this->writeExpression(*t.ifFalse());
1692 }
1693
1694 size_t nslots = ifTrue.slots();
1695 SkASSERT(nslots == ifFalse.slots());
1696
1697 Value result(nslots);
1698 for (size_t i = 0; i < nslots; ++i) {
1699 result[i] = skvm::select(test, i32(ifTrue[i]), i32(ifFalse[i]));
1700 }
1701 return result;
1702 }
1703
writeExpression(const Expression & e)1704 Value SkVMGenerator::writeExpression(const Expression& e) {
1705 switch (e.kind()) {
1706 case Expression::Kind::kBinary:
1707 return this->writeBinaryExpression(e.as<BinaryExpression>());
1708 case Expression::Kind::kChildCall:
1709 return this->writeChildCall(e.as<ChildCall>());
1710 case Expression::Kind::kConstructorArray:
1711 case Expression::Kind::kConstructorCompound:
1712 case Expression::Kind::kConstructorStruct:
1713 return this->writeAggregationConstructor(e.asAnyConstructor());
1714 case Expression::Kind::kConstructorArrayCast:
1715 return this->writeExpression(*e.as<ConstructorArrayCast>().argument());
1716 case Expression::Kind::kConstructorDiagonalMatrix:
1717 return this->writeConstructorDiagonalMatrix(e.as<ConstructorDiagonalMatrix>());
1718 case Expression::Kind::kConstructorMatrixResize:
1719 return this->writeConstructorMatrixResize(e.as<ConstructorMatrixResize>());
1720 case Expression::Kind::kConstructorScalarCast:
1721 case Expression::Kind::kConstructorCompoundCast:
1722 return this->writeConstructorCast(e.asAnyConstructor());
1723 case Expression::Kind::kConstructorSplat:
1724 return this->writeConstructorSplat(e.as<ConstructorSplat>());
1725 case Expression::Kind::kFieldAccess:
1726 return this->writeFieldAccess(e.as<FieldAccess>());
1727 case Expression::Kind::kIndex:
1728 return this->writeIndexExpression(e.as<IndexExpression>());
1729 case Expression::Kind::kVariableReference:
1730 return this->writeVariableExpression(e.as<VariableReference>());
1731 case Expression::Kind::kLiteral:
1732 return this->writeLiteral(e.as<Literal>());
1733 case Expression::Kind::kFunctionCall:
1734 return this->writeFunctionCall(e.as<FunctionCall>());
1735 case Expression::Kind::kExternalFunctionCall:
1736 return this->writeExternalFunctionCall(e.as<ExternalFunctionCall>());
1737 case Expression::Kind::kPrefix:
1738 return this->writePrefixExpression(e.as<PrefixExpression>());
1739 case Expression::Kind::kPostfix:
1740 return this->writePostfixExpression(e.as<PostfixExpression>());
1741 case Expression::Kind::kSwizzle:
1742 return this->writeSwizzle(e.as<Swizzle>());
1743 case Expression::Kind::kTernary:
1744 return this->writeTernaryExpression(e.as<TernaryExpression>());
1745 case Expression::Kind::kExternalFunctionReference:
1746 default:
1747 SkDEBUGFAIL("Unsupported expression");
1748 return {};
1749 }
1750 }
1751
writeStore(const Expression & lhs,const Value & rhs)1752 Value SkVMGenerator::writeStore(const Expression& lhs, const Value& rhs) {
1753 SkASSERTF(rhs.slots() == lhs.type().slotCount(),
1754 "lhs=%s (%s)\nrhs=%zu slot",
1755 lhs.type().description().c_str(), lhs.description().c_str(), rhs.slots());
1756
1757 // We need to figure out the collection of slots that we're storing into. The l-value (lhs)
1758 // is always a VariableReference, possibly wrapped by one or more Swizzle, FieldAccess, or
1759 // IndexExpressions. The underlying VariableReference has a range of slots for its storage,
1760 // and each expression wrapped around that selects a sub-set of those slots (Field/Index),
1761 // or rearranges them (Swizzle).
1762 SkSTArray<4, size_t, true> slots;
1763 slots.resize(rhs.slots());
1764
1765 // Start with the identity slot map - this basically says that the values from rhs belong in
1766 // slots [0, 1, 2 ... N] of the lhs.
1767 for (size_t i = 0; i < slots.size(); ++i) {
1768 slots[i] = i;
1769 }
1770
1771 // Now, as we peel off each outer expression, adjust 'slots' to be the locations relative to
1772 // the next (inner) expression:
1773 const Expression* expr = &lhs;
1774 while (!expr->is<VariableReference>()) {
1775 switch (expr->kind()) {
1776 case Expression::Kind::kFieldAccess: {
1777 const FieldAccess& fld = expr->as<FieldAccess>();
1778 size_t offset = this->fieldSlotOffset(fld);
1779 for (size_t& s : slots) {
1780 s += offset;
1781 }
1782 expr = fld.base().get();
1783 } break;
1784 case Expression::Kind::kIndex: {
1785 const IndexExpression& idx = expr->as<IndexExpression>();
1786 size_t offset = this->indexSlotOffset(idx);
1787 for (size_t& s : slots) {
1788 s += offset;
1789 }
1790 expr = idx.base().get();
1791 } break;
1792 case Expression::Kind::kSwizzle: {
1793 const Swizzle& swz = expr->as<Swizzle>();
1794 for (size_t& s : slots) {
1795 s = swz.components()[s];
1796 }
1797 expr = swz.base().get();
1798 } break;
1799 default:
1800 // No other kinds of expressions are valid in lvalues. (see Analysis::IsAssignable)
1801 SkDEBUGFAIL("Invalid expression type");
1802 return {};
1803 }
1804 }
1805
1806 // When we get here, 'slots' are all relative to the first slot holding 'var's storage
1807 const Variable& var = *expr->as<VariableReference>().variable();
1808 size_t varSlot = this->getSlot(var);
1809 for (size_t& slot : slots) {
1810 SkASSERT(slot < var.type().slotCount());
1811 slot += varSlot;
1812 }
1813
1814 // `slots` are now absolute indices into `fSlots`.
1815 skvm::I32 mask = this->mask();
1816 for (size_t i = 0; i < rhs.slots(); ++i) {
1817 int slotNum = slots[i];
1818 skvm::Val conditionalStore = this->writeConditionalStore(fSlots[slotNum].val, rhs[i], mask);
1819 this->writeToSlot(slotNum, conditionalStore);
1820 }
1821
1822 return rhs;
1823 }
1824
writeConditionalStore(skvm::Val lhs,skvm::Val rhs,skvm::I32 mask)1825 skvm::Val SkVMGenerator::writeConditionalStore(skvm::Val lhs, skvm::Val rhs, skvm::I32 mask) {
1826 return select(mask, f32(rhs), f32(lhs)).id;
1827 }
1828
writeBlock(const Block & b)1829 void SkVMGenerator::writeBlock(const Block& b) {
1830 skvm::I32 mask = this->mask();
1831 if (b.isScope()) {
1832 this->emitTraceScope(mask, +1);
1833 }
1834
1835 for (const std::unique_ptr<Statement>& stmt : b.children()) {
1836 this->writeStatement(*stmt);
1837 }
1838
1839 if (b.isScope()) {
1840 this->emitTraceScope(mask, -1);
1841 }
1842 }
1843
writeBreakStatement()1844 void SkVMGenerator::writeBreakStatement() {
1845 // Any active lanes stop executing for the duration of the current loop
1846 fLoopMask &= ~this->mask();
1847 }
1848
writeContinueStatement()1849 void SkVMGenerator::writeContinueStatement() {
1850 // Any active lanes stop executing for the current iteration.
1851 // Remember them in fContinueMask, to be re-enabled later.
1852 skvm::I32 mask = this->mask();
1853 fLoopMask &= ~mask;
1854 fContinueMask |= mask;
1855 }
1856
writeForStatement(const ForStatement & f)1857 void SkVMGenerator::writeForStatement(const ForStatement& f) {
1858 // We require that all loops be ES2-compliant (unrollable), and actually unroll them here
1859 SkASSERT(f.unrollInfo());
1860 const LoopUnrollInfo& loop = *f.unrollInfo();
1861 SkASSERT(loop.fIndex->type().slotCount() == 1);
1862
1863 size_t indexSlot = this->getSlot(*loop.fIndex);
1864 double val = loop.fStart;
1865
1866 const skvm::I32 zero = fBuilder->splat(0);
1867 skvm::I32 oldLoopMask = fLoopMask,
1868 oldContinueMask = fContinueMask;
1869
1870 const Type::NumberKind indexKind = base_number_kind(loop.fIndex->type());
1871
1872 // We want the loop index to disappear at the end of the loop, so wrap the for statement in a
1873 // trace scope.
1874 if (loop.fCount > 0) {
1875 skvm::I32 mask = this->mask();
1876 this->emitTraceScope(mask, +1);
1877
1878 for (int i = 0; i < loop.fCount; ++i) {
1879 this->writeToSlot(indexSlot, (indexKind == Type::NumberKind::kFloat)
1880 ? fBuilder->splat(static_cast<float>(val)).id
1881 : fBuilder->splat(static_cast<int>(val)).id);
1882
1883 fContinueMask = zero;
1884 this->writeStatement(*f.statement());
1885 fLoopMask |= fContinueMask;
1886
1887 this->emitTraceLine(f.test() ? f.test()->fLine : f.fLine);
1888 val += loop.fDelta;
1889 }
1890
1891 this->emitTraceScope(mask, -1);
1892 }
1893
1894 fLoopMask = oldLoopMask;
1895 fContinueMask = oldContinueMask;
1896 }
1897
writeIfStatement(const IfStatement & i)1898 void SkVMGenerator::writeIfStatement(const IfStatement& i) {
1899 Value test = this->writeExpression(*i.test());
1900 {
1901 ScopedCondition ifTrue(this, i32(test));
1902 this->writeStatement(*i.ifTrue());
1903 }
1904 if (i.ifFalse()) {
1905 ScopedCondition ifFalse(this, ~i32(test));
1906 this->writeStatement(*i.ifFalse());
1907 }
1908 }
1909
writeReturnStatement(const ReturnStatement & r)1910 void SkVMGenerator::writeReturnStatement(const ReturnStatement& r) {
1911 skvm::I32 returnsHere = this->mask();
1912
1913 if (r.expression()) {
1914 Value val = this->writeExpression(*r.expression());
1915
1916 size_t slot = currentFunction().fReturnSlot;
1917 size_t nslots = r.expression()->type().slotCount();
1918 for (size_t i = 0; i < nslots; ++i) {
1919 fSlots[slot + i].writtenTo = false;
1920 skvm::Val conditionalStore = this->writeConditionalStore(fSlots[slot + i].val, val[i],
1921 returnsHere);
1922 this->writeToSlot(slot + i, conditionalStore);
1923 }
1924 }
1925
1926 currentFunction().fReturned |= returnsHere;
1927 }
1928
writeSwitchStatement(const SwitchStatement & s)1929 void SkVMGenerator::writeSwitchStatement(const SwitchStatement& s) {
1930 skvm::I32 falseValue = fBuilder->splat( 0);
1931 skvm::I32 trueValue = fBuilder->splat(~0);
1932
1933 // Create a "switchFallthough" scratch variable, initialized to false.
1934 skvm::I32 switchFallthrough = falseValue;
1935
1936 // Loop masks behave just like for statements. When a break is encountered, it masks off all
1937 // lanes for the rest of the body of the switch.
1938 skvm::I32 oldLoopMask = fLoopMask;
1939 Value switchValue = this->writeExpression(*s.value());
1940
1941 for (const std::unique_ptr<Statement>& stmt : s.cases()) {
1942 const SwitchCase& c = stmt->as<SwitchCase>();
1943 if (!c.isDefault()) {
1944 Value caseValue = fBuilder->splat((int) c.value());
1945
1946 // We want to execute this switch case if we're falling through from a previous case, or
1947 // if the case value matches.
1948 ScopedCondition conditionalCaseBlock(
1949 this,
1950 switchFallthrough | (i32(caseValue) == i32(switchValue)));
1951 this->writeStatement(*c.statement());
1952
1953 // If we are inside the case block, we set the fallthrough flag to true (`break` still
1954 // works to stop the flow of execution regardless, since it zeroes out the loop-mask).
1955 switchFallthrough.id = this->writeConditionalStore(switchFallthrough.id, trueValue.id,
1956 this->mask());
1957 } else {
1958 // This is the default case. Since it's always last, we can just dump in the code.
1959 this->writeStatement(*c.statement());
1960 }
1961 }
1962
1963 // Restore state.
1964 fLoopMask = oldLoopMask;
1965 }
1966
writeVarDeclaration(const VarDeclaration & decl)1967 void SkVMGenerator::writeVarDeclaration(const VarDeclaration& decl) {
1968 size_t slot = this->getSlot(decl.var()),
1969 nslots = decl.var().type().slotCount();
1970
1971 Value val = decl.value() ? this->writeExpression(*decl.value()) : Value{};
1972 for (size_t i = 0; i < nslots; ++i) {
1973 fSlots[slot + i].writtenTo = false;
1974 this->writeToSlot(slot + i, val ? val[i] : fBuilder->splat(0.0f).id);
1975 }
1976 }
1977
emitTraceLine(int line)1978 void SkVMGenerator::emitTraceLine(int line) {
1979 if (fDebugTrace && line > 0) {
1980 fBuilder->trace_line(fTraceHookID, this->mask(), fTraceMask, line);
1981 }
1982 }
1983
emitTraceScope(skvm::I32 executionMask,int delta)1984 void SkVMGenerator::emitTraceScope(skvm::I32 executionMask, int delta) {
1985 if (fDebugTrace) {
1986 fBuilder->trace_scope(fTraceHookID, executionMask, fTraceMask, delta);
1987 }
1988 }
1989
writeStatement(const Statement & s)1990 void SkVMGenerator::writeStatement(const Statement& s) {
1991 this->emitTraceLine(s.fLine);
1992
1993 switch (s.kind()) {
1994 case Statement::Kind::kBlock:
1995 this->writeBlock(s.as<Block>());
1996 break;
1997 case Statement::Kind::kBreak:
1998 this->writeBreakStatement();
1999 break;
2000 case Statement::Kind::kContinue:
2001 this->writeContinueStatement();
2002 break;
2003 case Statement::Kind::kExpression:
2004 this->writeExpression(*s.as<ExpressionStatement>().expression());
2005 break;
2006 case Statement::Kind::kFor:
2007 this->writeForStatement(s.as<ForStatement>());
2008 break;
2009 case Statement::Kind::kIf:
2010 this->writeIfStatement(s.as<IfStatement>());
2011 break;
2012 case Statement::Kind::kReturn:
2013 this->writeReturnStatement(s.as<ReturnStatement>());
2014 break;
2015 case Statement::Kind::kSwitch:
2016 this->writeSwitchStatement(s.as<SwitchStatement>());
2017 break;
2018 case Statement::Kind::kVarDeclaration:
2019 this->writeVarDeclaration(s.as<VarDeclaration>());
2020 break;
2021 case Statement::Kind::kDiscard:
2022 case Statement::Kind::kDo:
2023 SkDEBUGFAIL("Unsupported control flow");
2024 break;
2025 case Statement::Kind::kInlineMarker:
2026 case Statement::Kind::kNop:
2027 break;
2028 default:
2029 SkDEBUGFAIL("Unrecognized statement");
2030 break;
2031 }
2032 }
2033
ProgramToSkVM(const Program & program,const FunctionDefinition & function,skvm::Builder * builder,SkVMDebugTrace * debugTrace,SkSpan<skvm::Val> uniforms,skvm::Coord device,skvm::Coord local,skvm::Color inputColor,skvm::Color destColor,SkVMCallbacks * callbacks)2034 skvm::Color ProgramToSkVM(const Program& program,
2035 const FunctionDefinition& function,
2036 skvm::Builder* builder,
2037 SkVMDebugTrace* debugTrace,
2038 SkSpan<skvm::Val> uniforms,
2039 skvm::Coord device,
2040 skvm::Coord local,
2041 skvm::Color inputColor,
2042 skvm::Color destColor,
2043 SkVMCallbacks* callbacks) {
2044 skvm::Val zero = builder->splat(0.0f).id;
2045 skvm::Val result[4] = {zero,zero,zero,zero};
2046
2047 skvm::Val args[8]; // At most 8 arguments (half4 srcColor, half4 dstColor)
2048 size_t argSlots = 0;
2049 for (const SkSL::Variable* param : function.declaration().parameters()) {
2050 switch (param->modifiers().fLayout.fBuiltin) {
2051 case SK_MAIN_COORDS_BUILTIN:
2052 SkASSERT(param->type().slotCount() == 2);
2053 SkASSERT((argSlots + 2) <= SK_ARRAY_COUNT(args));
2054 args[argSlots++] = local.x.id;
2055 args[argSlots++] = local.y.id;
2056 break;
2057 case SK_INPUT_COLOR_BUILTIN:
2058 SkASSERT(param->type().slotCount() == 4);
2059 SkASSERT((argSlots + 4) <= SK_ARRAY_COUNT(args));
2060 args[argSlots++] = inputColor.r.id;
2061 args[argSlots++] = inputColor.g.id;
2062 args[argSlots++] = inputColor.b.id;
2063 args[argSlots++] = inputColor.a.id;
2064 break;
2065 case SK_DEST_COLOR_BUILTIN:
2066 SkASSERT(param->type().slotCount() == 4);
2067 SkASSERT((argSlots + 4) <= SK_ARRAY_COUNT(args));
2068 args[argSlots++] = destColor.r.id;
2069 args[argSlots++] = destColor.g.id;
2070 args[argSlots++] = destColor.b.id;
2071 args[argSlots++] = destColor.a.id;
2072 break;
2073 default:
2074 SkDEBUGFAIL("Invalid parameter to main()");
2075 return {};
2076 }
2077 }
2078 SkASSERT(argSlots <= SK_ARRAY_COUNT(args));
2079
2080 // Make sure that the SkVMDebugTrace starts from a clean slate.
2081 if (debugTrace) {
2082 debugTrace->fSlotInfo.clear();
2083 debugTrace->fFuncInfo.clear();
2084 debugTrace->fTraceInfo.clear();
2085 }
2086
2087 SkVMGenerator generator(program, builder, debugTrace, callbacks);
2088 generator.writeProgram(uniforms, device, function, {args, argSlots}, SkMakeSpan(result));
2089
2090 return skvm::Color{{builder, result[0]},
2091 {builder, result[1]},
2092 {builder, result[2]},
2093 {builder, result[3]}};
2094 }
2095
ProgramToSkVM(const Program & program,const FunctionDefinition & function,skvm::Builder * b,SkVMDebugTrace * debugTrace,SkSpan<skvm::Val> uniforms,SkVMSignature * outSignature)2096 bool ProgramToSkVM(const Program& program,
2097 const FunctionDefinition& function,
2098 skvm::Builder* b,
2099 SkVMDebugTrace* debugTrace,
2100 SkSpan<skvm::Val> uniforms,
2101 SkVMSignature* outSignature) {
2102 SkVMSignature ignored,
2103 *signature = outSignature ? outSignature : &ignored;
2104
2105 std::vector<skvm::Ptr> argPtrs;
2106 std::vector<skvm::Val> argVals;
2107
2108 for (const Variable* p : function.declaration().parameters()) {
2109 size_t slots = p->type().slotCount();
2110 signature->fParameterSlots += slots;
2111 for (size_t i = 0; i < slots; ++i) {
2112 argPtrs.push_back(b->varying<float>());
2113 argVals.push_back(b->loadF(argPtrs.back()).id);
2114 }
2115 }
2116
2117 std::vector<skvm::Ptr> returnPtrs;
2118 std::vector<skvm::Val> returnVals;
2119
2120 signature->fReturnSlots = function.declaration().returnType().slotCount();
2121 for (size_t i = 0; i < signature->fReturnSlots; ++i) {
2122 returnPtrs.push_back(b->varying<float>());
2123 returnVals.push_back(b->splat(0.0f).id);
2124 }
2125
2126 class Callbacks : public SkVMCallbacks {
2127 public:
2128 Callbacks(skvm::Color color) : fColor(color) {}
2129
2130 skvm::Color sampleShader(int, skvm::Coord) override {
2131 fUsedUnsupportedFeatures = true;
2132 return fColor;
2133 }
2134 skvm::Color sampleColorFilter(int, skvm::Color) override {
2135 fUsedUnsupportedFeatures = true;
2136 return fColor;
2137 }
2138 skvm::Color sampleBlender(int, skvm::Color, skvm::Color) override {
2139 fUsedUnsupportedFeatures = true;
2140 return fColor;
2141 }
2142
2143 skvm::Color toLinearSrgb(skvm::Color) override {
2144 fUsedUnsupportedFeatures = true;
2145 return fColor;
2146 }
2147 skvm::Color fromLinearSrgb(skvm::Color) override {
2148 fUsedUnsupportedFeatures = true;
2149 return fColor;
2150 }
2151
2152 bool fUsedUnsupportedFeatures = false;
2153 const skvm::Color fColor;
2154 };
2155
2156 // Set up device coordinates so that the rightmost evaluated pixel will be centered on (0, 0).
2157 // (If the coordinates aren't used, dead-code elimination will optimize this away.)
2158 skvm::F32 pixelCenter = b->splat(0.5f);
2159 skvm::Coord device = {pixelCenter, pixelCenter};
2160 device.x += to_F32(b->splat(1) - b->index());
2161
2162 skvm::F32 zero = b->splat(0.0f);
2163 skvm::Color sampledColor{zero, zero, zero, zero};
2164 Callbacks callbacks(sampledColor);
2165
2166 SkVMGenerator generator(program, b, debugTrace, &callbacks);
2167 generator.writeProgram(uniforms, device, function, SkMakeSpan(argVals), SkMakeSpan(returnVals));
2168
2169 // If the SkSL tried to use any shader, colorFilter, or blender objects - we don't have a
2170 // mechanism (yet) for binding to those.
2171 if (callbacks.fUsedUnsupportedFeatures) {
2172 return false;
2173 }
2174
2175 // generateCode has updated the contents of 'argVals' for any 'out' or 'inout' parameters.
2176 // Propagate those changes back to our varying buffers:
2177 size_t argIdx = 0;
2178 for (const Variable* p : function.declaration().parameters()) {
2179 size_t nslots = p->type().slotCount();
2180 if (p->modifiers().fFlags & Modifiers::kOut_Flag) {
2181 for (size_t i = 0; i < nslots; ++i) {
2182 b->storeF(argPtrs[argIdx + i], skvm::F32{b, argVals[argIdx + i]});
2183 }
2184 }
2185 argIdx += nslots;
2186 }
2187
2188 // It's also updated the contents of 'returnVals' with the return value of the entry point.
2189 // Store that as well:
2190 for (size_t i = 0; i < signature->fReturnSlots; ++i) {
2191 b->storeF(returnPtrs[i], skvm::F32{b, returnVals[i]});
2192 }
2193
2194 return true;
2195 }
2196
Program_GetFunction(const Program & program,const char * function)2197 const FunctionDefinition* Program_GetFunction(const Program& program, const char* function) {
2198 for (const ProgramElement* e : program.elements()) {
2199 if (e->is<FunctionDefinition>() &&
2200 e->as<FunctionDefinition>().declaration().name() == function) {
2201 return &e->as<FunctionDefinition>();
2202 }
2203 }
2204 return nullptr;
2205 }
2206
gather_uniforms(UniformInfo * info,const Type & type,const std::string & name)2207 static void gather_uniforms(UniformInfo* info, const Type& type, const std::string& name) {
2208 switch (type.typeKind()) {
2209 case Type::TypeKind::kStruct:
2210 for (const auto& f : type.fields()) {
2211 gather_uniforms(info, *f.fType, name + "." + std::string(f.fName));
2212 }
2213 break;
2214 case Type::TypeKind::kArray:
2215 for (int i = 0; i < type.columns(); ++i) {
2216 gather_uniforms(info, type.componentType(),
2217 String::printf("%s[%d]", name.c_str(), i));
2218 }
2219 break;
2220 case Type::TypeKind::kScalar:
2221 case Type::TypeKind::kVector:
2222 case Type::TypeKind::kMatrix:
2223 info->fUniforms.push_back({name, base_number_kind(type), type.rows(), type.columns(),
2224 info->fUniformSlotCount});
2225 info->fUniformSlotCount += type.columns() * type.rows();
2226 break;
2227 default:
2228 break;
2229 }
2230 }
2231
Program_GetUniformInfo(const Program & program)2232 std::unique_ptr<UniformInfo> Program_GetUniformInfo(const Program& program) {
2233 auto info = std::make_unique<UniformInfo>();
2234 for (const ProgramElement* e : program.elements()) {
2235 if (!e->is<GlobalVarDeclaration>()) {
2236 continue;
2237 }
2238 const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
2239 const Variable& var = decl.declaration()->as<VarDeclaration>().var();
2240 if (var.modifiers().fFlags & Modifiers::kUniform_Flag) {
2241 gather_uniforms(info.get(), var.type(), std::string(var.name()));
2242 }
2243 }
2244 return info;
2245 }
2246
2247 /*
2248 * Testing utility function that emits program's "main" with a minimal harness. Used to create
2249 * representative skvm op sequences for SkSL tests.
2250 */
testingOnly_ProgramToSkVMShader(const Program & program,skvm::Builder * builder,SkVMDebugTrace * debugTrace)2251 bool testingOnly_ProgramToSkVMShader(const Program& program,
2252 skvm::Builder* builder,
2253 SkVMDebugTrace* debugTrace) {
2254 const SkSL::FunctionDefinition* main = Program_GetFunction(program, "main");
2255 if (!main) {
2256 return false;
2257 }
2258
2259 size_t uniformSlots = 0;
2260 int childSlots = 0;
2261 for (const SkSL::ProgramElement* e : program.elements()) {
2262 if (e->is<GlobalVarDeclaration>()) {
2263 const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
2264 const Variable& var = decl.declaration()->as<VarDeclaration>().var();
2265 if (var.type().isEffectChild()) {
2266 childSlots++;
2267 } else if (is_uniform(var)) {
2268 uniformSlots += var.type().slotCount();
2269 }
2270 }
2271 }
2272
2273 skvm::Uniforms uniforms(builder->uniform(), 0);
2274
2275 auto new_uni = [&]() { return builder->uniformF(uniforms.pushF(0.0f)); };
2276
2277 // Assume identity CTM
2278 skvm::Coord device = {pun_to_F32(builder->index()), new_uni()};
2279 skvm::Coord local = device;
2280
2281 class Callbacks : public SkVMCallbacks {
2282 public:
2283 Callbacks(skvm::Builder* builder, skvm::Uniforms* uniforms, int numChildren) {
2284 for (int i = 0; i < numChildren; ++i) {
2285 fChildren.push_back(
2286 {uniforms->pushPtr(nullptr), builder->uniform32(uniforms->push(0))});
2287 }
2288 }
2289
2290 skvm::Color sampleShader(int i, skvm::Coord coord) override {
2291 skvm::PixelFormat pixelFormat = skvm::SkColorType_to_PixelFormat(kRGBA_F32_SkColorType);
2292 skvm::I32 index = trunc(coord.x);
2293 index += trunc(coord.y) * fChildren[i].rowBytesAsPixels;
2294 return gather(pixelFormat, fChildren[i].addr, index);
2295 }
2296
2297 skvm::Color sampleColorFilter(int i, skvm::Color color) override {
2298 return color;
2299 }
2300
2301 skvm::Color sampleBlender(int i, skvm::Color src, skvm::Color dst) override {
2302 return blend(SkBlendMode::kSrcOver, src, dst);
2303 }
2304
2305 // TODO(skia:10479): Make these actually convert to/from something like sRGB, for use in
2306 // test files.
2307 skvm::Color toLinearSrgb(skvm::Color color) override {
2308 return color;
2309 }
2310 skvm::Color fromLinearSrgb(skvm::Color color) override {
2311 return color;
2312 }
2313
2314 struct Child {
2315 skvm::Uniform addr;
2316 skvm::I32 rowBytesAsPixels;
2317 };
2318 std::vector<Child> fChildren;
2319 };
2320 Callbacks callbacks(builder, &uniforms, childSlots);
2321
2322 std::vector<skvm::Val> uniformVals;
2323 for (size_t i = 0; i < uniformSlots; ++i) {
2324 uniformVals.push_back(new_uni().id);
2325 }
2326
2327 skvm::Color inColor = builder->uniformColor(SkColors::kWhite, &uniforms);
2328 skvm::Color destColor = builder->uniformColor(SkColors::kBlack, &uniforms);
2329
2330 skvm::Color result = SkSL::ProgramToSkVM(program, *main, builder, debugTrace,
2331 SkMakeSpan(uniformVals), device, local, inColor,
2332 destColor, &callbacks);
2333
2334 storeF(builder->varying<float>(), result.r);
2335 storeF(builder->varying<float>(), result.g);
2336 storeF(builder->varying<float>(), result.b);
2337 storeF(builder->varying<float>(), result.a);
2338
2339 return true;
2340 }
2341
2342 } // namespace SkSL
2343