centralize test/bench SkVM builders
Eliminate the duplicate functionality,
and better testing for the bench builders.
Change-Id: If20e52107738903f854aec431416e573d7a7d640
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/218041
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index b5efce4..3fba86e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1665,6 +1665,13 @@
}
}
+ test_lib("skvm_builders") {
+ sources = [
+ "tools/SkVMBuilders.cpp",
+ "tools/SkVMBuilders.h",
+ ]
+ }
+
import("gn/tests.gni")
test_lib("tests") {
sources = tests_sources + pathops_tests_sources
@@ -1681,6 +1688,7 @@
":experimental_svg_model",
":flags",
":skia",
+ ":skvm_builders",
":tool_utils",
"modules/skottie:tests",
"modules/sksg:tests",
@@ -1702,6 +1710,7 @@
":gm",
":gpu_tool_utils",
":skia",
+ ":skvm_builders",
":tool_utils",
]
}
diff --git a/bench/SkVMBench.cpp b/bench/SkVMBench.cpp
index eae0b70..32708ac 100644
--- a/bench/SkVMBench.cpp
+++ b/bench/SkVMBench.cpp
@@ -8,124 +8,13 @@
#include "bench/Benchmark.h"
#include "src/core/SkOpts.h"
#include "src/core/SkVM.h"
+#include "tools/SkVMBuilders.h"
namespace {
enum Mode {Opts, RP, F32, I32, I32_SWAR};
static const char* kMode_name[] = { "Opts", "RP","F32", "I32", "I32_SWAR" };
- struct SrcoverBuilder_F32 : public skvm::Builder {
- SrcoverBuilder_F32() {
-
- skvm::Arg src = arg(0),
- dst = arg(1);
-
- auto byte_to_f32 = [&](skvm::I32 byte) {
- return mul(splat(1/255.0f), to_f32(byte));
- };
- auto f32_to_byte = [&](skvm::F32 f32) {
- return to_i32(mad(f32, splat(255.0f), splat(0.5f)));
- };
-
- auto load = [&](skvm::Arg ptr,
- skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) {
- skvm::I32 rgba = load32(ptr);
- *r = byte_to_f32(bit_and( rgba , splat(0xff)));
- *g = byte_to_f32(bit_and(shr(rgba, 8), splat(0xff)));
- *b = byte_to_f32(bit_and(shr(rgba, 16), splat(0xff)));
- *a = byte_to_f32( shr(rgba, 24) );
- };
-
- skvm::F32 r,g,b,a;
- load(src, &r,&g,&b,&a);
-
- skvm::F32 dr,dg,db,da;
- load(dst, &dr,&dg,&db,&da);
-
- skvm::F32 invA = sub(splat(1.0f), a);
- r = mad(dr, invA, r);
- g = mad(dg, invA, g);
- b = mad(db, invA, b);
- a = mad(da, invA, a);
-
- store32(dst, bit_or( f32_to_byte(r) ,
- bit_or(shl(f32_to_byte(g), 8),
- bit_or(shl(f32_to_byte(b), 16),
- shl(f32_to_byte(a), 24)))));
- }
- };
-
- struct SrcoverBuilder_I32 : public skvm::Builder {
- SrcoverBuilder_I32() {
- skvm::Arg src = arg(0),
- dst = arg(1);
-
- auto load = [&](skvm::Arg ptr,
- skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) {
- skvm::I32 rgba = load32(ptr);
- *r = bit_and( rgba , splat(0xff));
- *g = bit_and(shr(rgba, 8), splat(0xff));
- *b = bit_and(shr(rgba, 16), splat(0xff));
- *a = shr(rgba, 24) ;
- };
-
- auto mul_unorm8 = [&](skvm::I32 x, skvm::I32 y) {
- // (x*y + 127)/255 ~= (x*y+255)/256
- return shr(add(mul(x, y), splat(0xff)), 8);
- };
-
- skvm::I32 r,g,b,a;
- load(src, &r,&g,&b,&a);
-
- skvm::I32 dr,dg,db,da;
- load(dst, &dr,&dg,&db,&da);
-
- skvm::I32 invA = sub(splat(0xff), a);
- r = add(r, mul_unorm8(dr, invA));
- g = add(g, mul_unorm8(dg, invA));
- b = add(b, mul_unorm8(db, invA));
- a = add(a, mul_unorm8(da, invA));
-
- store32(dst, bit_or( r ,
- bit_or(shl(g, 8),
- bit_or(shl(b, 16),
- shl(a, 24)))));
- }
- };
-
- struct SrcoverBuilder_I32_SWAR : public skvm::Builder {
- SrcoverBuilder_I32_SWAR() {
- skvm::Arg src = arg(0),
- dst = arg(1);
-
- auto load = [&](skvm::Arg ptr,
- skvm::I32* rb, skvm::I32* ga) {
- skvm::I32 rgba = load32(ptr);
- *rb = bit_and( rgba, splat(0x00ff00ff));
- *ga = bit_and(shr(rgba, 8), splat(0x00ff00ff));
- };
-
- auto mul_unorm8 = [&](skvm::I32 x, skvm::I32 y) {
- // As above, assuming x is two SWAR bytes in lanes 0 and 2, and y is a byte.
- return bit_and(shr(add(mul(x, y),
- splat(0x00ff00ff)),
- 8),
- splat(0x00ff00ff));
- };
-
- skvm::I32 rb, ga;
- load(src, &rb, &ga);
-
- skvm::I32 drb, dga;
- load(dst, &drb, &dga);
-
- skvm::I32 invA = sub(splat(0xff), shr(ga, 16));
- rb = add(rb, mul_unorm8(drb, invA));
- ga = add(ga, mul_unorm8(dga, invA));
-
- store32(dst, bit_or(rb, shl(ga, 8)));
- }
- };
}
class SkVMBench : public Benchmark {
diff --git a/resources/SkVMTest.expected b/resources/SkVMTest.expected
index b40149b..da98689 100644
--- a/resources/SkVMTest.expected
+++ b/resources/SkVMTest.expected
@@ -278,3 +278,71 @@
r8 = bit_or r8 r0
store32 arg(1) r8
+I32 8888 over 8888
+r0 = load32 arg(0)
+r1 = splat FF (3.5733111e-43)
+r2 = bit_and r0 r1
+r3 = shr r0 8 (1.1210388e-44)
+r3 = bit_and r3 r1
+r4 = shr r0 10 (2.2420775e-44)
+r4 = bit_and r4 r1
+r0 = shr r0 18 (3.3631163e-44)
+r5 = load32 arg(1)
+r6 = bit_and r5 r1
+r7 = shr r5 8 (1.1210388e-44)
+r7 = bit_and r7 r1
+r8 = shr r5 10 (2.2420775e-44)
+r8 = bit_and r8 r1
+r5 = shr r5 18 (3.3631163e-44)
+r9 = sub_i32 r1 r0
+r6 = mul_i32 r6 r9
+r6 = add_i32 r6 r1
+r6 = shr r6 8 (1.1210388e-44)
+r6 = add_i32 r2 r6
+r7 = mul_i32 r7 r9
+r7 = add_i32 r7 r1
+r7 = shr r7 8 (1.1210388e-44)
+r7 = add_i32 r3 r7
+r8 = mul_i32 r8 r9
+r8 = add_i32 r8 r1
+r8 = shr r8 8 (1.1210388e-44)
+r8 = add_i32 r4 r8
+r9 = mul_i32 r5 r9
+r9 = add_i32 r9 r1
+r9 = shr r9 8 (1.1210388e-44)
+r9 = add_i32 r0 r9
+r7 = shl r7 8 (1.1210388e-44)
+r7 = bit_or r6 r7
+r8 = shl r8 10 (2.2420775e-44)
+r8 = bit_or r7 r8
+r9 = shl r9 18 (3.3631163e-44)
+r9 = bit_or r8 r9
+store32 arg(1) r9
+
+I32 (SWAR) 8888 over 8888
+r0 = load32 arg(0)
+r1 = splat FF00FF (2.3418409e-38)
+r2 = bit_and r0 r1
+r0 = shr r0 8 (1.1210388e-44)
+r0 = bit_and r0 r1
+r3 = load32 arg(1)
+r4 = bit_and r3 r1
+r3 = shr r3 8 (1.1210388e-44)
+r3 = bit_and r3 r1
+r5 = splat FF (3.5733111e-43)
+r6 = shr r0 10 (2.2420775e-44)
+r6 = sub_i32 r5 r6
+r4 = mul_i32 r4 r6
+r4 = add_i32 r4 r1
+r4 = shr r4 8 (1.1210388e-44)
+r4 = bit_and r4 r1
+r4 = add_i32 r2 r4
+r6 = mul_i32 r3 r6
+r6 = add_i32 r6 r1
+r6 = shr r6 8 (1.1210388e-44)
+r6 = bit_and r6 r1
+r6 = add_i32 r0 r6
+r6 = shl r6 8 (1.1210388e-44)
+r6 = bit_or r4 r6
+store32 arg(1) r6
+
diff --git a/tests/SkVMTest.cpp b/tests/SkVMTest.cpp
index 7d8c0b0..df608ab 100644
--- a/tests/SkVMTest.cpp
+++ b/tests/SkVMTest.cpp
@@ -10,117 +10,27 @@
#include "src/core/SkVM.h"
#include "tests/Test.h"
#include "tools/Resources.h"
+#include "tools/SkVMBuilders.h"
-enum Fmt { A8, G8, RGBA_8888 };
+using Fmt = SrcoverBuilder_F32::Fmt;
const char* fmt_name(Fmt fmt) {
switch (fmt) {
- case A8: return "A8";
- case G8: return "G8";
- case RGBA_8888: return "RGBA_8888";
+ case Fmt::A8: return "A8";
+ case Fmt::G8: return "G8";
+ case Fmt::RGBA_8888: return "RGBA_8888";
}
return "";
}
-// Here's a cute little trick that avoids the need to explicitly thread
-// and skvm::Builder* through and make a lot of builder->foo() calls.
-// Instead the builder becomes this, with this-> omitted for clarity.
-//
-// Think of this as
-// static void srcover(skvm::Builder*, Fmt srcFmt, Fmt dstFmt) { ... }
-//
-// Some parts of this builder code are written less fluently than possible,
-// to avoid any ambiguity of function argument evaluation order. This lets
-// our golden tests work portably. In general there's no reason to fear
-// nesting calls to Builder routines.
-
-struct SrcoverBuilder : public skvm::Builder {
- SrcoverBuilder(Fmt srcFmt, Fmt dstFmt) {
- skvm::Arg src = arg(0),
- dst = arg(1);
-
- auto byte_to_f32 = [&](skvm::I32 byte) {
- skvm::F32 _1_255 = splat(1/255.0f);
- return mul(_1_255, to_f32(byte));
- };
-
- auto load = [&](skvm::Arg ptr, Fmt fmt,
- skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) {
- switch (fmt) {
- case A8: {
- *r = *g = *b = splat(0.0f);
- *a = byte_to_f32(load8(ptr));
- } break;
-
- case G8: {
- *r = *g = *b = byte_to_f32(load8(ptr));
- *a = splat(1.0f);
- } break;
-
- case RGBA_8888: {
- skvm::I32 rgba = load32(ptr),
- _255 = splat(255);
- *r = byte_to_f32(bit_and( rgba , _255));
- *g = byte_to_f32(bit_and(shr(rgba, 8), _255));
- *b = byte_to_f32(bit_and(shr(rgba, 16), _255));
- *a = byte_to_f32( shr(rgba, 24) );
- } break;
- }
- };
-
- skvm::F32 r,g,b,a;
- load(src, srcFmt, &r,&g,&b,&a);
-
- skvm::F32 dr,dg,db,da;
- load(dst, dstFmt, &dr,&dg,&db,&da);
-
- skvm::F32 invA = sub(splat(1.0f), a);
- r = mad(dr, invA, r);
- g = mad(dg, invA, g);
- b = mad(db, invA, b);
- a = mad(da, invA, a);
-
- auto f32_to_byte = [&](skvm::F32 f32) {
- skvm::F32 _255 = splat(255.0f),
- _0_5 = splat(0.5f);
- return to_i32(mad(f32, _255, _0_5));
- };
- switch (dstFmt) {
- case A8: {
- store8(dst, f32_to_byte(a));
- } break;
-
- case G8: {
- skvm::F32 _2126 = splat(0.2126f),
- _7152 = splat(0.7152f),
- _0722 = splat(0.0722f);
- store8(dst, f32_to_byte(mad(r, _2126,
- mad(g, _7152,
- mul(b, _0722)))));
- } break;
-
- case RGBA_8888: {
- skvm::I32 R = f32_to_byte(r) ,
- G = shl(f32_to_byte(g), 8),
- B = shl(f32_to_byte(b), 16),
- A = shl(f32_to_byte(a), 24);
-
- R = bit_or(R,G);
- R = bit_or(R,B);
- R = bit_or(R,A);
-
- store32(dst, R);
- } break;
- }
- }
-};
-
DEF_TEST(SkVM, r) {
SkDynamicMemoryWStream buf;
+
+ // Write all combinations of SrcoverBuilder_F32
for (int s = 0; s < 3; s++)
for (int d = 0; d < 3; d++) {
auto srcFmt = (Fmt)s,
dstFmt = (Fmt)d;
- skvm::Program program = SrcoverBuilder{srcFmt, dstFmt}.done();
+ skvm::Program program = SrcoverBuilder_F32{srcFmt, dstFmt}.done();
buf.writeText(fmt_name(srcFmt));
buf.writeText(" over ");
@@ -130,6 +40,20 @@
buf.writeText("\n");
}
+ // Write the I32 Srcovers also.
+ {
+ skvm::Program program = SrcoverBuilder_I32{}.done();
+ buf.writeText("I32 8888 over 8888\n");
+ program.dump(&buf);
+ buf.writeText("\n");
+ }
+ {
+ skvm::Program program = SrcoverBuilder_I32_SWAR{}.done();
+ buf.writeText("I32 (SWAR) 8888 over 8888\n");
+ program.dump(&buf);
+ buf.writeText("\n");
+ }
+
sk_sp<SkData> blob = buf.detachAsData();
{
@@ -145,7 +69,7 @@
}
{
- skvm::Program program = SrcoverBuilder{RGBA_8888, RGBA_8888}.done();
+ skvm::Program program = SrcoverBuilder_F32{Fmt::RGBA_8888, Fmt::RGBA_8888}.done();
uint32_t src = 0xbb007733,
dst = 0xffaaccee;
@@ -164,7 +88,45 @@
}
{
- skvm::Program program = SrcoverBuilder{RGBA_8888, G8}.done();
+ skvm::Program program = SrcoverBuilder_I32{}.done();
+
+ uint32_t src = 0xbb007733,
+ dst = 0xffaaccee;
+ SkPMColor want = SkPMSrcOver(src, dst); // 0xff2dad73
+
+ program.eval(1, &src, &dst);
+
+ // dst is probably 0xff2dad72.
+ for (int i = 0; i < 4; i++) {
+ uint8_t d = dst & 0xff,
+ w = want & 0xff;
+ REPORTER_ASSERT(r, abs(d-w) < 2);
+ dst >>= 8;
+ want >>= 8;
+ }
+ }
+
+ {
+ skvm::Program program = SrcoverBuilder_I32_SWAR{}.done();
+
+ uint32_t src = 0xbb007733,
+ dst = 0xffaaccee;
+ SkPMColor want = SkPMSrcOver(src, dst); // 0xff2dad73
+
+ program.eval(1, &src, &dst);
+
+ // dst is probably 0xff2dad72.
+ for (int i = 0; i < 4; i++) {
+ uint8_t d = dst & 0xff,
+ w = want & 0xff;
+ REPORTER_ASSERT(r, abs(d-w) < 2);
+ dst >>= 8;
+ want >>= 8;
+ }
+ }
+
+ {
+ skvm::Program program = SrcoverBuilder_F32{Fmt::RGBA_8888, Fmt::G8}.done();
uint32_t src = 0xbb007733;
uint8_t dst = 0x42;
@@ -179,7 +141,7 @@
}
{
- skvm::Program program = SrcoverBuilder{A8, A8}.done();
+ skvm::Program program = SrcoverBuilder_F32{Fmt::A8, Fmt::A8}.done();
uint8_t src[256],
dst[256];
diff --git a/tools/SkVMBuilders.cpp b/tools/SkVMBuilders.cpp
new file mode 100644
index 0000000..ff58bf2
--- /dev/null
+++ b/tools/SkVMBuilders.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "tools/SkVMBuilders.h"
+
+// Some parts of this builder code are written less fluently than possible,
+// to avoid any ambiguity of function argument evaluation order. This lets
+// our golden tests work portably. In general there's no reason to fear
+// nesting calls to Builder routines.
+
+SrcoverBuilder_F32::SrcoverBuilder_F32(Fmt srcFmt, Fmt dstFmt) {
+ skvm::Arg src = arg(0),
+ dst = arg(1);
+
+ auto byte_to_f32 = [&](skvm::I32 byte) {
+ skvm::F32 _1_255 = splat(1/255.0f);
+ return mul(_1_255, to_f32(byte));
+ };
+
+ auto load = [&](skvm::Arg ptr, Fmt fmt,
+ skvm::F32* r, skvm::F32* g, skvm::F32* b, skvm::F32* a) {
+ switch (fmt) {
+ case Fmt::A8: {
+ *r = *g = *b = splat(0.0f);
+ *a = byte_to_f32(load8(ptr));
+ } break;
+
+ case Fmt::G8: {
+ *r = *g = *b = byte_to_f32(load8(ptr));
+ *a = splat(1.0f);
+ } break;
+
+ case Fmt::RGBA_8888: {
+ skvm::I32 rgba = load32(ptr),
+ _255 = splat(255);
+ *r = byte_to_f32(bit_and( rgba , _255));
+ *g = byte_to_f32(bit_and(shr(rgba, 8), _255));
+ *b = byte_to_f32(bit_and(shr(rgba, 16), _255));
+ *a = byte_to_f32( shr(rgba, 24) );
+ } break;
+ }
+ };
+
+ skvm::F32 r,g,b,a;
+ load(src, srcFmt, &r,&g,&b,&a);
+
+ skvm::F32 dr,dg,db,da;
+ load(dst, dstFmt, &dr,&dg,&db,&da);
+
+ skvm::F32 invA = sub(splat(1.0f), a);
+ r = mad(dr, invA, r);
+ g = mad(dg, invA, g);
+ b = mad(db, invA, b);
+ a = mad(da, invA, a);
+
+ auto f32_to_byte = [&](skvm::F32 f32) {
+ skvm::F32 _255 = splat(255.0f),
+ _0_5 = splat(0.5f);
+ return to_i32(mad(f32, _255, _0_5));
+ };
+ switch (dstFmt) {
+ case Fmt::A8: {
+ store8(dst, f32_to_byte(a));
+ } break;
+
+ case Fmt::G8: {
+ skvm::F32 _2126 = splat(0.2126f),
+ _7152 = splat(0.7152f),
+ _0722 = splat(0.0722f);
+ store8(dst, f32_to_byte(mad(r, _2126,
+ mad(g, _7152,
+ mul(b, _0722)))));
+ } break;
+
+ case Fmt::RGBA_8888: {
+ skvm::I32 R = f32_to_byte(r) ,
+ G = shl(f32_to_byte(g), 8),
+ B = shl(f32_to_byte(b), 16),
+ A = shl(f32_to_byte(a), 24);
+
+ R = bit_or(R,G);
+ R = bit_or(R,B);
+ R = bit_or(R,A);
+
+ store32(dst, R);
+ } break;
+ }
+}
+
+SrcoverBuilder_I32::SrcoverBuilder_I32() {
+ skvm::Arg src = arg(0),
+ dst = arg(1);
+
+ auto load = [&](skvm::Arg ptr,
+ skvm::I32* r, skvm::I32* g, skvm::I32* b, skvm::I32* a) {
+ skvm::I32 rgba = load32(ptr),
+ mask = splat(0xff);
+ *r = bit_and( rgba , mask);
+ *g = bit_and(shr(rgba, 8), mask);
+ *b = bit_and(shr(rgba, 16), mask);
+ *a = shr(rgba, 24) ;
+ };
+
+ auto mul_unorm8 = [&](skvm::I32 x, skvm::I32 y) {
+ // (x*y + 127)/255 ~= (x*y+255)/256
+ skvm::I32 _255 = splat(255);
+ return shr(add(mul(x, y), _255), 8);
+ };
+
+ skvm::I32 r,g,b,a;
+ load(src, &r,&g,&b,&a);
+
+ skvm::I32 dr,dg,db,da;
+ load(dst, &dr,&dg,&db,&da);
+
+ skvm::I32 invA = sub(splat(0xff), a);
+ r = add(r, mul_unorm8(dr, invA));
+ g = add(g, mul_unorm8(dg, invA));
+ b = add(b, mul_unorm8(db, invA));
+ a = add(a, mul_unorm8(da, invA));
+
+ r = bit_or(r, shl(g, 8));
+ r = bit_or(r, shl(b, 16));
+ r = bit_or(r, shl(a, 24));
+
+ store32(dst, r);
+}
+
+SrcoverBuilder_I32_SWAR::SrcoverBuilder_I32_SWAR() {
+ skvm::Arg src = arg(0),
+ dst = arg(1);
+
+ auto load = [&](skvm::Arg ptr,
+ skvm::I32* rb, skvm::I32* ga) {
+ skvm::I32 rgba = load32(ptr),
+ mask = splat(0x00ff00ff);
+ *rb = bit_and( rgba, mask);
+ *ga = bit_and(shr(rgba, 8), mask);
+ };
+
+ auto mul_unorm8 = [&](skvm::I32 x, skvm::I32 y) {
+ // As above, assuming x is two SWAR bytes in lanes 0 and 2, and y is a byte.
+ skvm::I32 _255 = splat(0x00ff00ff);
+ return bit_and(shr(add(mul(x, y),
+ _255),
+ 8),
+ _255);
+ };
+
+ skvm::I32 rb, ga;
+ load(src, &rb, &ga);
+
+ skvm::I32 drb, dga;
+ load(dst, &drb, &dga);
+
+ skvm::I32 _255 = splat(0xff),
+ invA = sub(_255, shr(ga, 16));
+ rb = add(rb, mul_unorm8(drb, invA));
+ ga = add(ga, mul_unorm8(dga, invA));
+
+ store32(dst, bit_or(rb, shl(ga, 8)));
+}
diff --git a/tools/SkVMBuilders.h b/tools/SkVMBuilders.h
new file mode 100644
index 0000000..9d57837
--- /dev/null
+++ b/tools/SkVMBuilders.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkVMBuilders_DEFINED
+#define SkVMBuilders_DEFINED
+
+#include "src/core/SkVM.h"
+
+// SkVM builders used by both SkVMBench.cpp and SkVMTest.cpp.
+
+struct SrcoverBuilder_F32 : public skvm::Builder {
+ enum class Fmt { A8, G8, RGBA_8888 };
+ SrcoverBuilder_F32(Fmt srcFmt = Fmt::RGBA_8888,
+ Fmt dstFmt = Fmt::RGBA_8888);
+};
+
+struct SrcoverBuilder_I32 : public skvm::Builder {
+ SrcoverBuilder_I32(); // 8888 over 8888
+};
+
+struct SrcoverBuilder_I32_SWAR : public skvm::Builder {
+ SrcoverBuilder_I32_SWAR(); // 8888 over 8888
+};
+
+#endif//SkVMBuilders_DEFINED