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