Interpreter: Optimize common swizzles
Generates better code for the runtime color filter, for example.
Change-Id: Id86dfdbd93b2419cc2f72245fe33d5109b1ca59d
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/215834
Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
index 16d43f4..28aa1cd 100644
--- a/src/sksl/SkSLByteCodeGenerator.cpp
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -134,6 +134,27 @@
}
}
+// A "simple" Swizzle is based on a variable (or a compound variable like a struct or array), and
+// that references consecutive values, such that it can be implemented using normal load/store ops
+// with an offset. Note that all single-component swizzles (of suitable base types) are simple.
+static bool swizzle_is_simple(const Swizzle& s) {
+ switch (s.fBase->fKind) {
+ case Expression::kFieldAccess_Kind:
+ case Expression::kIndex_Kind:
+ case Expression::kVariableReference_Kind:
+ break;
+ default:
+ return false;
+ }
+
+ for (size_t i = 1; i < s.fComponents.size(); ++i) {
+ if (s.fComponents[i] != s.fComponents[i - 1] + 1) {
+ return false;
+ }
+ }
+ return true;
+}
+
int ByteCodeGenerator::getLocation(const Variable& var) {
// given that we seldom have more than a couple of variables, linear search is probably the most
// efficient way to handle lookups
@@ -192,6 +213,7 @@
}
}
+// TODO: Elide Add 0 and Mul 1 sequences
int ByteCodeGenerator::getLocation(const Expression& expr, Variable::Storage* storage) {
switch (expr.fKind) {
case Expression::kFieldAccess_Kind: {
@@ -237,6 +259,20 @@
this->write(ByteCodeInstruction::kAddI);
return -1;
}
+ case Expression::kSwizzle_Kind: {
+ const Swizzle& s = (const Swizzle&)expr;
+ SkASSERT(swizzle_is_simple(s));
+ int baseAddr = this->getLocation(*s.fBase, storage);
+ int offset = s.fComponents[0];
+ if (baseAddr < 0) {
+ this->write(ByteCodeInstruction::kPushImmediate);
+ this->write32(offset);
+ this->write(ByteCodeInstruction::kAddI);
+ return -1;
+ } else {
+ return baseAddr + offset;
+ }
+ }
case Expression::kVariableReference_Kind: {
const Variable& var = ((const VariableReference&)expr).fVariable;
*storage = var.fStorage;
@@ -619,6 +655,11 @@
}
void ByteCodeGenerator::writeSwizzle(const Swizzle& s) {
+ if (swizzle_is_simple(s)) {
+ this->writeVariableExpression(s);
+ return;
+ }
+
switch (s.fBase->fKind) {
case Expression::kVariableReference_Kind: {
const Variable& var = ((VariableReference&) *s.fBase).fVariable;
@@ -825,8 +866,12 @@
case Expression::kIndex_Kind:
case Expression::kVariableReference_Kind:
return std::unique_ptr<LValue>(new ByteCodeExpressionLValue(this, e));
- case Expression::kSwizzle_Kind:
- return std::unique_ptr<LValue>(new ByteCodeSwizzleLValue(this, (Swizzle&) e));
+ case Expression::kSwizzle_Kind: {
+ const Swizzle& s = (const Swizzle&) e;
+ return swizzle_is_simple(s)
+ ? std::unique_ptr<LValue>(new ByteCodeExpressionLValue(this, e))
+ : std::unique_ptr<LValue>(new ByteCodeSwizzleLValue(this, s));
+ }
case Expression::kTernary_Kind:
default:
printf("unsupported lvalue %s\n", e.description().c_str());