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